在 Java 集合框架中,Stack 是一种基于后进先出(LIFO, Last-In-First-Out) 原则的线性数据结构,主要用于实现栈的相关操作。
栈结构如下图:

注意,Stack 并非独立设计,而是直接继承 Vector(Vector 是 Java 早期的动态数组实现类),本质上是通过 Vector 的动态数组机制存储元素。这意味着 Stack 天然具备 Vector 的特性,例如:动态扩容(数组满时自动增长)、支持通过索引访问元素等,但也继承了 Vector 的设计局限性(如暴露非栈操作方法)。
Stack 类支持元素的压栈(push)、出栈(pop)、查看栈顶元素(peek)等操作。
由于 Stack 直接继承自 Vector,因此底层是基于数组实现,本质上是通过动态数组来存储元素。
线程安全:由于继承自 Vector,所有方法都具有同步特性,适合多线程环境。
操作简单:提供了直观的栈操作方法,易于理解和使用。
性能问题:同步操作会带来额外的性能开销,在单线程环境中效率较低。
设计缺陷:继承 Vector 导致 Stack 暴露了所有 Vector 的方法(如 add(int, E)),破坏了栈的封装性(理论上栈只能从顶端操作)。
注意,由于 Stack 存在性能问题和设计缺陷,在实际开发中,推荐使用 Deque 接口的实现类(如 ArrayDeque)替代 Stack,原因如下:
Deque 提供了更规范的栈操作方法(push()、pop()、peek())。
ArrayDeque 是非同步的,性能优于 Stack。
封装性更好,不会暴露额外的操作方法。
例如:
import java.util.ArrayDeque;
import java.util.Deque;
public class DequeExample {
public static void main(String[] args) {
Deque<Integer> stack = new ArrayDeque<>();
stack.push(10);
stack.push(20);
System.out.println(stack.pop()); // 输出: 20
}
}
Stack 类的主要方法如下:
push(E item) 将元素压入栈顶,返回被压入的元素。
pop() 移除并返回栈顶元素,栈为空时抛出 EmptyStackException。
peek() 返回栈顶元素(不移除),栈为空时抛出 EmptyStackException。
empty() 判断栈是否为空,为空返回 true 无。
search(Object(Object(int o) 返回指定元素在栈中的位置(从栈顶开始计数),元素不存在时返回 -1。
下面通过一个示例介绍 Stack 类的基础用法,如压栈、出栈等操作,如下:
import java.util.Stack;
public class StackExample {
public static void main(String[] args) {
// 创建栈对象
Stack<Integer> stack = new Stack<>();
// 压栈操作
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("栈元素: " + stack); // 输出: [10, 20, 30]
// 查看栈顶元素
System.out.println("栈顶元素: " + stack.peek()); // 输出: 30
// 出栈操作
int top = stack.pop();
System.out.println("出栈元素: " + top); // 输出: 30
System.out.println("出栈后栈元素: " + stack); // 输出: [10, 20]
// 判断栈是否为空
System.out.println("栈是否为空: " + stack.empty()); // 输出: false
// 查找元素位置
int index = stack.search(10);
System.out.println("元素10的位置: " + index); // 输出: 2(从栈顶开始计数)
}
}运行结果:
栈元素: [10, 20, 30] 栈顶元素: 30 出栈元素: 30 出栈后栈元素: [10, 20] 栈是否为空: false 元素10的位置: 2
下面通过分析 Stack 源码,了解 Stack 的实现原理:
package java.util;
public class Stack<E> extends Vector<E> {
/** 用于JDK 1.0.2版本的序列化兼容性 */
@java.io.Serial
private static final long serialVersionUID = 1224463164541339165L;
/**
* 创建一个空栈
*/
public Stack() {
}
/**
* 将元素压入栈顶。此操作与以下代码效果完全相同:
* addElement(item)
*
* @param item 要压入栈的元素
* @return 被压入的元素(即参数 {@code item})
* @see java.util.Vector#addElement
*/
public E push(E item) {
// 调用父类 Vector 的 addElement 方法,将元素添加到末尾(栈顶)
addElement(item);
return item;
}
/**
* 移除栈顶的对象,并将该对象作为此方法的返回值
*
* @return 栈顶的对象(即Vector对象的最后一个元素)
* @throws EmptyStackException 如果栈为空时调用此方法
*/
public synchronized E pop() {
E obj;
int len = size(); // 获取当前栈的长度
obj = peek(); // 先获取栈顶元素(会检查栈是否为空)
removeElementAt(len - 1); // 移除栈顶元素(Vector的最后一个元素)
return obj;
}
/**
* 查看栈顶的对象,但不从栈中移除它
*
* @return 栈顶的对象(即Vector对象的最后一个元素)
* @throws EmptyStackException 如果栈为空时调用此方法
*/
public synchronized E peek() {
int len = size(); // 获取当前栈的长度
if (len == 0)
throw new EmptyStackException(); // 栈为空时抛出异常
return elementAt(len - 1); // 返回Vector的最后一个元素(栈顶)
}
/**
* 测试此栈是否为空
*
* @return 当且仅当栈不包含任何元素时返回 true;否则返回 false;
*/
public boolean empty() {
return size() == 0; // 通过判断 Vector 的大小是否为 0 来确定栈是否为空
}
/**
* 返回对象在栈中的 1-based(从1开始)位置。
* 如果对象 o 是栈中的元素,此方法返回该元素距离栈顶的距离。
* 栈顶元素的距离被视为 1。此方法使用 equals 方法来比较 o 与栈中的元素。
*
* @param o 要查找的对象
* @return 从栈顶开始计算的 1-based 位置;返回值 -1 表示该对象不在栈中
*/
public synchronized int search(Object o) {
// 调用 Vector 的 lastIndexOf 方法,获取对象在向量中最后一次出现的索引(从0开始)
int i = lastIndexOf(o);
if (i >= 0) {
// 转换为 1-based 的栈顶距离(栈顶为1)
return size() - i;
}
return -1; // 未找到对象
}
}更多信息请参考 https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html API 文档。