1. 概述
本文将快速介绍 java.util.Stack
类及其核心用法。栈(Stack)是一种通用的数据结构,遵循 LIFO(后进先出)原则,支持在常数时间内压入(push)和弹出(pop)元素。
⚠️ 重要提示:对于新项目,优先使用 Deque
接口及其实现类(如 ArrayDeque
)。Deque
提供了更完整和一致的 LIFO 操作集。但在处理遗留代码时,我们仍可能遇到 Stack
类,因此深入理解它很有必要。
2. 创建栈
使用无参构造函数创建空栈实例:
@Test
public void whenStackIsCreated_thenItHasSizeZero() {
Stack<Integer> intStack = new Stack<>();
assertEquals(0, intStack.size());
}
这会创建一个默认容量为 10 的栈。当元素数量超过容量时,栈会自动扩容(容量翻倍),但移除元素后容量不会收缩。
3. 栈的同步性
Stack
直接继承自 Vector
,这意味着它和父类一样是同步实现。但在不需要同步的场景下,建议使用 ArrayDeque
以避免不必要的性能开销。
4. 向栈添加元素
使用 push()
方法将元素压入栈顶,该方法会返回被添加的元素:
@Test
public void whenElementIsPushed_thenStackSizeIsIncreased() {
Stack<Integer> intStack = new Stack<>();
intStack.push(1);
assertEquals(1, intStack.size());
}
push()
与 addElement()
效果相同,区别在于后者返回操作结果而非被添加元素。
也可以批量添加元素:
@Test
public void whenMultipleElementsArePushed_thenStackSizeIsIncreased() {
Stack<Integer> intStack = new Stack<>();
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
boolean result = intStack.addAll(intList);
assertTrue(result);
assertEquals(7, intList.size());
}
5. 从栈获取元素
5.1 弹出元素(移除并返回)
使用 pop()
移除并返回栈顶元素:
@Test
public void whenElementIsPoppedFromStack_thenElementIsRemovedAndSizeChanges() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
Integer element = intStack.pop();
assertEquals(Integer.valueOf(5), element);
assertTrue(intStack.isEmpty());
}
5.2 查看栈顶元素(不移除)
使用 peek()
获取栈顶元素但不移除:
@Test
public void whenElementIsPeeked_thenElementIsNotRemovedAndSizeDoesNotChange() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
Integer element = intStack.peek();
assertEquals(Integer.valueOf(5), element);
assertEquals(1, intStack.search(5));
assertEquals(1, intStack.size());
}
6. 在栈中搜索元素
6.1 搜索位置
search()
返回元素到栈顶的距离(栈顶位置为 1):
@Test
public void whenElementIsOnStack_thenSearchReturnsItsDistanceFromTheTop() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
intStack.push(8);
assertEquals(2, intStack.search(5));
}
✅ 关键点:
- 若存在多个相同元素,返回最靠近栈顶的元素位置
- 栈顶元素位置为 1
- 未找到元素时返回 -1
6.2 获取元素索引
使用 indexOf()
和 lastIndexOf()
获取元素索引:
@Test
public void whenElementIsOnStack_thenIndexOfReturnsItsIndex() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
int indexOf = intStack.indexOf(5);
assertEquals(0, indexOf);
}
lastIndexOf()
始终返回最靠近栈顶的元素索引,与 search()
类似但返回索引值(从 0 开始):
@Test
public void whenMultipleElementsAreOnStack_thenIndexOfReturnsLastElementIndex() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
intStack.push(5);
intStack.push(5);
int lastIndexOf = intStack.lastIndexOf(5);
assertEquals(2, lastIndexOf);
}
7. 从栈移除元素
7.1 移除指定元素
使用 removeElement()
移除首个匹配元素:
@Test
public void whenRemoveElementIsInvoked_thenElementIsRemoved() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
intStack.push(5);
intStack.removeElement(5);
assertEquals(1, intStack.size());
}
使用 removeElementAt()
按索引移除:
@Test
public void whenRemoveElementAtIsInvoked_thenElementIsRemoved() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
intStack.push(7);
intStack.removeElementAt(1);
assertEquals(-1, intStack.search(7));
}
7.2 批量移除元素
使用 removeAll()
移除集合中所有匹配元素:
@Test
public void givenElementsOnStack_whenRemoveAllIsInvoked_thenAllElementsFromCollectionAreRemoved() {
Stack<Integer> intStack = new Stack<>();
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
intStack.addAll(intList);
intStack.add(500);
intStack.removeAll(intList);
assertEquals(1, intStack.size());
assertEquals(1, intStack.search(500));
}
使用 clear()
或 removeAllElements()
清空栈:
@Test
public void whenRemoveAllElementsIsInvoked_thenAllElementsAreRemoved() {
Stack<Integer> intStack = new Stack<>();
intStack.push(5);
intStack.push(7);
intStack.removeAllElements();
assertTrue(intStack.isEmpty());
}
7.3 按条件移除元素
使用 removeIf()
结合条件表达式移除元素:
@Test
public void whenRemoveIfIsInvoked_thenAllElementsSatysfyingConditionAreRemoved() {
Stack<Integer> intStack = new Stack<>();
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
intStack.addAll(intList);
intStack.removeIf(element -> element < 6);
assertEquals(2, intStack.size());
}
8. 遍历栈
支持 Iterator
和 ListIterator
遍历:
Iterator
:单向遍历ListIterator
:双向遍历
@Test
public void whenAnotherStackCreatedWhileTraversingStack_thenStacksAreEqual() {
Stack<Integer> intStack = new Stack<>();
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
intStack.addAll(intList);
ListIterator<Integer> it = intStack.listIterator();
Stack<Integer> result = new Stack<>();
while(it.hasNext()) {
result.push(it.next());
}
assertThat(result, equalTo(intStack));
}
⚠️ 所有 Stack
返回的迭代器都是 fail-fast 的。
9. 使用 Stream API
作为 Collection
的子类,Stack
支持 Java 8 Stream API:
@Test
public void whenStackIsFiltered_allElementsNotSatisfyingFilterConditionAreDiscarded() {
Stack<Integer> intStack = new Stack<>();
List<Integer> inputIntList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 9, 10);
intStack.addAll(inputIntList);
List<Integer> filtered = intStack
.stream()
.filter(element -> element <= 3)
.collect(Collectors.toList());
assertEquals(3, filtered.size());
}
10. 总结
本文快速介绍了 Java 核心类 Stack
的关键特性和用法。完整 API 可参考 Javadoc,所有示例代码见 GitHub 仓库。