Java Stack(栈)教程

更新于 2025-12-28

Jakob Jenkov 2020-05-21

Java 的 Stack 类(java.util.Stack)是一个经典的栈数据结构。你可以将元素压入(push)到 Java Stack 的顶部,也可以从顶部弹出(pop)元素,即读取并移除栈顶的元素。

实际上,Java 的 Stack 类实现了 Java List 接口,但你很少会把 Stack 当作 List 来使用——除非你需要检查当前栈中存储的所有元素。

请注意:Java 的 Stack 类是 Vector 的子类,而 Vector 是一个较老的、线程安全的 Java 类。这种同步机制会给所有方法调用带来轻微的性能开销。此外,Vector 使用了一些过时(不再推荐)的 Java 特性,比如已被 Iterator 接口取代的 Enumeration。如果你希望避免这些问题,可以改用 Java Deque 作为栈来使用。

Java Stack 基础知识

Stack 是一种数据结构,你将元素添加到“栈顶”,也从栈顶移除元素。这被称为 “后进先出”(LIFO, Last In First Out) 原则。

相比之下,Java Queue(队列) 遵循 “先进先出”(FIFO, First In First Out) 原则:元素从队列尾部加入,从队列头部移除。


创建一个 Stack

要使用 Java 的 Stack,首先需要创建一个 Stack 实例。例如:

Stack stack = new Stack();

使用泛型类型创建 Stack

你可以为 Stack 指定泛型类型,以限制该 Stack 实例只能包含特定类型的对象。在声明 Stack 变量时指定泛型类型。例如:

Stack<String> stack = new Stack<String>();

上述 Stack 只能包含 String 类型的实例。

建议:为 Stack 使用泛型类型,这样可以简化代码(访问栈中对象时无需强制类型转换),并降低误将错误类型对象压入栈的风险。


向栈中压入元素(Push)

创建了 Stack 实例后,就可以使用 push() 方法将元素压入栈顶。注意,压入的必须是 Java 对象。

示例:

Stack<String> stack = new Stack<String>();
stack.push("1");

这段代码将一个值为 "1"String 对象压入栈顶。


从栈中弹出元素(Pop)

一旦元素被压入 Stack,就可以通过 pop() 方法将其弹出。弹出后,该元素将从栈中移除,新的栈顶元素就是之前被压入的倒数第二个元素。

示例:

Stack<String> stack = new Stack<String>();
stack.push("1");
String topElement = stack.pop();

执行后,topElement 的值为 "1",且该元素已从栈中移除。


查看栈顶元素(Peek)

Stack 类提供了一个 peek() 方法,用于查看栈顶元素而不将其弹出。

示例:

Stack<String> stack = new Stack<String>();
stack.push("1");
String topElement = stack.peek();

此时 topElement"1",但该元素仍然保留在栈中。


在栈中搜索元素

你可以使用 search() 方法在栈中查找某个对象,并返回其位置(索引)。该方法会调用对象的 equals() 方法进行比较。

注意:返回的索引是从栈顶开始计算的,栈顶元素的索引为 1。

示例:

Stack<String> stack = new Stack<String>();
stack.push("1");
stack.push("2");
stack.push("3");
int index = stack.search("3"); // index = 1(因为 "3" 在栈顶)

⚠️ 注意原文此处有误,实际 search("3") 应返回 1,而非 3。因为 "3" 是最后压入的,位于栈顶。


获取栈的大小

使用 size() 方法可以获取当前栈中元素的数量:

Stack<String> stack = new Stack<String>();
stack.push("1");
stack.push("2");
stack.push("3");
int size = stack.size(); // size = 3

遍历栈中的元素

可以通过 iterator() 方法获取一个 Java Iterator,从而遍历栈中所有元素:

Stack<String> stack = new Stack<String>();
stack.push("123");
stack.push("456");
stack.push("789");

Iterator<String> iterator = stack.iterator();
while (iterator.hasNext()) {
    String value = iterator.next();
    // 处理 value
}

⚠️ 注意:Iterator 遍历顺序是从栈底到栈顶(即压入顺序),而非 LIFO 顺序。


使用 Stream 处理栈

你也可以通过 stream() 方法获取一个 Java Stream,然后使用函数式方式处理栈中元素:

Stack<String> stack = new Stack<String>();
stack.push("A");
stack.push("B");
stack.push("C");

Stream<String> stream = stack.stream();
stream.forEach(element -> {
    System.out.println(element); // 打印每个元素
});

此例使用了 Java Lambda 表达式


使用 Stack 反转 List

你可以利用 Stack 的 LIFO 特性来反转一个 Java List

List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
System.out.println(list); // [A, B, C]

Stack<String> stack = new Stack<String>();
while (list.size() > 0) {
    stack.push(list.remove(0)); // 从 list 头部移除并压入栈
}

while (stack.size() > 0) {
    list.add(stack.pop()); // 从栈弹出并添加到 list 尾部
}

System.out.println(list); // [C, B, A]

使用 Java Deque 作为栈

如前所述,你可以使用 Java Deque 代替 StackDeque 提供了 push()pop() 方法,用法几乎相同:

Deque<String> dequeAsStack = new ArrayDeque<String>();
dequeAsStack.push("one");
dequeAsStack.push("two");
dequeAsStack.push("three");

String one = dequeAsStack.pop();   // "three"
String two = dequeAsStack.pop();   // "two"
String three = dequeAsStack.pop(); // "one"

✅ 推荐:在新代码中优先使用 Deque(如 ArrayDeque)代替 Stack,以获得更好的性能和现代 API 支持。


Stack 的典型应用场景

Stack 在某些数据处理场景中非常有用,例如:

  • 使用 SAXStAX 解析 XML 文件时,用于跟踪嵌套层级。
  • 表达式求值(如逆波兰表达式)。
  • 撤销(Undo)操作的历史记录。
  • 深度优先搜索(DFS)算法。