Java中应避免使用过时的Stack类,改用ArrayDeque或LinkedList实现Deque接口;Queue接口优先选择ArrayDeque,因其性能最优;Deque可同时支持栈和队列操作,但建议语义统一以避免混淆。

Java里没有直接的 Stack 和 Queue 接口实现类能“开箱即用”——Stack 类已过时,Queue 是接口,必须选具体实现。别用 Stack,改用 Deque;队列优先选 ArrayDeque 或 LinkedList。
为什么不该用 java.util.Stack
Stack 继承自 Vector,是线程安全但性能差的遗留类,所有方法都加了 synchronized;它违背了“栈只应支持 LIFO 操作”的设计原则,还暴露了 Vector 的冗余方法(如 elementAt()、removeAllElements())。
替代方案:ArrayDeque(推荐)或 LinkedList,二者都实现了 Deque 接口,提供标准栈操作:
-
push(e)→ 入栈(等价于addFirst(e)) -
pop()→ 出栈(等价于removeFirst()),空时抛NoSuchElementException -
peek()→ 查看栈顶(等价于getFirst()),空时也抛异常
示例:
立即学习“Java免费学习笔记(深入)”;
Dequestack = new ArrayDeque<>(); stack.push(1); stack.push(2); System.out.println(stack.pop()); // 输出 2
Queue 接口该用哪个实现类
Queue 是接口,不能 new。常见实现有三个,适用场景不同:
-
ArrayDeque:非线程安全,基于循环数组,入队/出队均摊 O(1),无容量限制,**首选**(除非需要线程安全或延迟队列语义) -
LinkedList:也实现Queue,但底层是链表,内存开销略大,性能略逊于ArrayDeque;仅在需频繁插入/删除中间节点时才考虑 -
PriorityQueue:不是 FIFO,而是按优先级排序;若你想要“最小元素先出”,才用它;否则别选
注意:offer(e)(成功返回 true,失败如满队列返回 false)比 add(e)(失败直接抛 IllegalStateException)更安全;poll() 和 peek() 同理,比 remove()/element() 更健壮。
Deque 同时当栈和队列用没问题吗
可以,而且正是它的设计本意。Deque(double-ended queue)既支持栈操作(push/pop/peek),也支持队列操作(offer/poll/peek),甚至双向操作(addLast/removeFirst 等)。
但要注意命名歧义:
-
peek()在栈语境下是看栈顶,在队列语境下是看队首——其实是同一个动作(看头部) -
poll()总是从头部移除;若想从尾部出队,得用pollLast() - 混用时容易逻辑混乱,建议一个变量只固定一种语义:要么全当栈(只用
push/pop),要么全当队列(只用offer/poll)
别写这种易错代码:
Dequedq = new ArrayDeque<>(); dq.push("a"); // 当栈用 dq.offer("b"); // 突然当队列用 → "b" 被加到尾部,但你可能以为在栈顶
空集合操作的异常行为必须检查
几乎所有栈/队列操作在空时都有明确异常策略,但不统一:
-
pop()、remove()、element():空时抛NoSuchElementException -
peek()、poll():空时返回null(引用类型)或0/false(基本类型包装时需拆箱注意 NPE)
所以实际编码中,除非你确定非空,否则优先用 poll() / peek() + null 判断,而不是依赖 try-catch 捕获 NoSuchElementException——后者是异常流,性能差且掩盖逻辑问题。
最容易被忽略的是泛型擦除后对 null 的误判:比如 Deque 中 poll() 返回 null,但如果你写了 int x = dq.poll();,会触发自动拆箱,空时直接抛 NullPointerException,不是 NoSuchElementException。









