不该用 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 换成:
Deque<string> stack = new ArrayDeque();</string>
对应操作直接映射:
-
stack.push("a")→ 仍可用(ArrayDeque实现了push()) -
stack.pop()→ 返回栈顶并移除,行为一致 -
stack.peek()→ 查看栈顶不移除,和Stack一样 -
stack.isEmpty()→ 判空,无差异
注意:ArrayDeque 不支持 null 元素,而 Stack 可以——如果业务逻辑真依赖存 null,说明栈设计本身就有问题,该重构。
遇到 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 特性
}</e></e></e>
关键点:
- 重写所有方法,确保内部只走
ArrayDeque - 保留
synchronized仅为了满足Stack接口签名,实际已无同步必要 - 绝不在外部暴露
delegate或调用super的Vector方法
真正难的不是语法,是意识到:栈不是“能后进先出就行”,而是“接口干净、行为可预测、扩展无隐患”。用错容器,后面 debug 时看到 Stack.size() 返回 5 但 peek() 却抛异常,就只能翻源码了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











