1. 概述
Stack(栈)遵循后进先出(LIFO)原则,正确打印其值有时会踩坑。别担心,本文将彻底解决这个问题!
我们将探索从最简单到最高效的多种打印方式,分析各方法的适用场景,并解释为什么在现代Java开发中,Deque(双端队列)比Stack更优。
2. 使用 Stack.toString()
只需快速查看栈内容时,toString()
是你的利器。它内置于Stack类(继承自Vector),能直接输出所有元素。
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
实现示例:
public static void givenStack_whenUsingToString_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println(stack.toString());
}
输出结果:
[10, 20, 30]
✅ 适用场景:
- 快速调试时检查栈内容
- 不关心格式,只需快速浏览元素
⚠️ 注意:
- 输出包含方括号(可能不符合需求)
- 输出格式像List,无法明确体现栈结构
3. 使用增强for循环
需要自定义输出格式时,循环遍历是简单粗暴的选择:
public static void givenStack_whenUsingForEach_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
for (Integer value : stack) {
System.out.print(value + " ");
}
}
输出结果:
10 20 30
✅ 适用场景:
- 需要更简洁或自定义的打印格式
- 元素顺序要求不严格时
❌ 局限:
- 不保证LIFO顺序(实际按插入顺序打印)
4. 使用 forEach() 循环
Java 8的forEach()
提供更简洁写法,但有个关键坑:Stack或Deque的forEach()不会按LIFO顺序打印! 它遵循集合的迭代顺序(即插入顺序)。
示例代码:
public static void givenStack_whenUsingDirectForEach_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
stack.forEach(element -> System.out.println(element));
}
输出结果:
10
20
30
⚠️ 重要提醒:
- 虽然提供完全的格式控制,但破坏了LIFO顺序
- 若需LIFO顺序,必须先反转栈:
public static void givenStack_whenUsingStreamReverse_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(20);
stack.push(10);
stack.push(30);
Collections.reverse(stack);
stack.forEach(System.out::println);
}
修正后输出:
30
10
20
5. 使用 Iterator
Iterator提供逐元素遍历的能力:
public static void givenStack_whenUsingIterator_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
Iterator<Integer> iterator = stack.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
}
输出结果:
10 20 30
✅ 适用场景:
- 需要比基础循环更多灵活性(如遍历时删除元素)
❌ 局限:
- 仍不保证LIFO顺序
- 比简单循环增加复杂度
6. 使用 ListIterator 获取真实LIFO顺序
必须严格按LIFO顺序打印时,ListIterator是最佳方案:
public static void givenStack_whenUsingListIteratorReverseOrder_thenPrintStack() {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
ListIterator<Integer> iterator = stack.listIterator(stack.size());
while (iterator.hasPrevious()) {
System.out.print(iterator.previous() + " ");
}
}
输出结果(严格LIFO顺序):
30 20 10
✅ 核心优势:
- 不修改栈结构即可正确打印LIFO顺序
- 专为Stack类设计,避免元素误删
❌ 局限:
- 代码量稍多
- 仅适用于Stack,不兼容Deque
7. 性能最优解:Deque
频繁操作栈时,强烈建议用Deque(如ArrayDeque)替代Stack。它更快、更高效,是现代Java的首选。
public static void givenStack_whenUsingDeque_thenPrintStack() {
Deque<Integer> stack = new ArrayDeque<>();
stack.push(10);
stack.push(20);
stack.push(30);
stack.forEach(e -> System.out.print(e + " "));
}
输出结果(天然LIFO顺序):
30 20 10
✅ 核心优势:
- 性能碾压Stack(尤其高并发场景)
- 天然保持LIFO顺序
- 现代Java开发首选
- 更好的并发支持
⚠️ 迁移成本:
- 需将现有Stack代码重构为Deque
8. 按场景选择最佳方案
根据需求快速决策:
场景 | 推荐方案 |
---|---|
✅ 快速调试 | toString() |
✅ 自定义格式 | 增强for循环或Java 8 forEach() |
✅ Stack的LIFO顺序 | ListIterator |
✅ 追求极致性能 | Deque (替代Stack) |
9. 总结
打印栈值不仅是展示元素,更是选择合适方案的艺术:
- 快速查看?
toString()
足够 - 需要格式控制?
forEach()
和Iterator
提供灵活性 - 必须LIFO顺序?
ListIterator
是Stack的最佳选择 - 追求性能?直接用
Deque
替代Stack
最终选择取决于具体场景需求。记住:现代Java中,Deque才是栈操作的正道!
本文所有代码可在 GitHub 获取。