不该用 Stack 类,因其继承过时的 Vector、同步开销大、破坏封装(如 get(i) 可随意访问中间元素);应改用 ArrayDeque 实现 Deque 接口,性能更好、API 更纯粹。

Java 的 Stack 类不推荐用于新代码,它继承自过时的 Vector,线程安全但性能差,且设计违反封装原则(比如能用 get(i) 随意访问中间元素)。
为什么不该用 Stack 类
它本质是带额外方法的 Vector,所有操作都加了 synchronized,即使单线程也白耗开销;push() 和 pop() 底层调用的是 addElement() 和 removeElementAt(size()-1),语义冗余;更严重的是,它公开了 elementAt()、setSize() 等破坏栈抽象的方法。
实际项目中,JDK 官方文档明确建议用 Deque 实现栈,比如 ArrayDeque —— 它更快、更轻量、API 更纯粹。
用 ArrayDeque 替代 Stack 的正确写法
把 Stack<string> stack = new Stack();</string> 换成:
立即学习“Java免费学习笔记(深入)”;
Deque<String> stack = new ArrayDeque<>();
对应操作直接映射:
-
stack.push("a")→ 仍可用(ArrayDeque实现了push()) -
stack.pop()→ 返回栈顶并移除,行为一致 -
stack.peek()→ 查看栈顶不移除,和Stack一样 -
stack.isEmpty()→ 判空,无差异
注意:ArrayDeque 不支持 null 元素,而 Stack 可以——如果业务逻辑真依赖存 null,说明栈设计本身就有问题,该重构。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
遇到 EmptyStackException 怎么办
无论是 Stack.pop() 还是 ArrayDeque.pop(),在空时都会抛 EmptyStackException。这不是 bug,是契约行为。
安全做法永远是先判空再操作:
if (!stack.isEmpty()) {
String top = stack.pop();
}
别依赖 try-catch 来控制流程——这会掩盖真实异常,且性能差。有些旧代码用 stack.size() > 0 判断,对 ArrayDeque 没问题,但 size() 对某些 Deque 实现(如并发场景下的 ConcurrentLinkedDeque)可能不精确,所以坚持用 isEmpty()。
如果必须兼容老 Stack 接口怎么办
比如对接遗留系统或第三方库强制要求 Stack 类型参数。这时不要继承 Stack 或包装它,而是用适配器模式最小化侵入:
public class DequeStack<E> extends Stack<E> {
private final Deque<E> delegate = new ArrayDeque<>();
@Override
public E push(E item) {
delegate.push(item);
return item;
}
@Override
public synchronized E pop() {
if (delegate.isEmpty()) throw new EmptyStackException();
return delegate.pop();
}
// 其他方法同理,只转发到 delegate,屏蔽 Vector 特性
}
关键点:
- 重写所有方法,确保内部只走
ArrayDeque - 保留
synchronized仅为了满足Stack接口签名,实际已无同步必要 - 绝不在外部暴露
delegate或调用super的Vector方法
真正难的不是语法,是意识到:栈不是“能后进先出就行”,而是“接口干净、行为可预测、扩展无隐患”。用错容器,后面 debug 时看到 Stack.size() 返回 5 但 peek() 却抛异常,就只能翻源码了。

















