由于 java.util.Stack 是一个遗留类,在现代 Java 开发中已不推荐使用,因此我们将重点放在推荐使用的 Deque (双端队列) 接口来实现栈和队列的功能。Deque 提供了更丰富、更统一的 API。


核心接口:Deque<E>

Deque (Double-Ended Queue) 是一个双端队列,它允许在队列的两端(头部和尾部)进行元素的插入和删除。通过限制只在一端进行操作,我们就可以很方便地实现栈和队列。

  • 实现栈 (Stack):只使用 Deque 的尾部 (Tail) 进行插入和删除操作。
  • 实现队列 (Queue):只使用 Deque 的尾部 (Tail) 进行插入,头部 (Head) 进行删除操作。

最常用的 Deque 实现类是 ArrayDeque 和 LinkedList

  • ArrayDeque: 基于动态数组实现,性能更高,是推荐的默认选择
  • LinkedList: 基于双向链表实现,在某些特定场景(如频繁在中间插入 / 删除)有优势。

一、栈 (Stack) 常用方法

使用 Deque 实现栈时,我们主要关注操作尾部 (Tail) 的方法。

方法 作用 异常处理 推荐度
push(e) 压栈:将元素添加到栈顶(Deque 的尾部)。 如果栈已满(对于有界 Deque),抛出 IllegalStateException 极高 (最常用的入栈方法)
pop() 弹栈:移除并返回栈顶(Deque 的尾部)的元素。 如果栈为空,抛出 NoSuchElementException 极高 (最常用的出栈方法)
peek() 查看栈顶:返回栈顶(Deque 的尾部)的元素,但不移除它。 如果栈为空,返回 null 极高 (安全地查看栈顶元素)

代码示例:

import java.util.Deque;
import java.util.ArrayDeque;

public class StackExample {
    public static void main(String[] args) {
        // 使用 ArrayDeque 作为栈
        Deque<Integer> stack = new ArrayDeque<>();

        // push() - 入栈
        stack.push(10);
        stack.push(20);
        stack.push(30);
        System.out.println("Stack after pushes: " + stack); // [10, 20, 30] (注意: toString()显示顺序是头到尾,栈顶是30)

        // peek() - 查看栈顶
        System.out.println("Top element (peek): " + stack.peek()); // 30
        System.out.println("Stack after peek: " + stack); // [10, 20, 30] (不变)

        // pop() - 出栈
        System.out.println("Popped element: " + stack.pop()); // 30
        System.out.println("Stack after pop: " + stack); // [10, 20]
    }
}

二、队列 (Queue) 常用方法

使用 Deque 实现队列时,我们主要关注尾部插入,头部删除

方法 作用 异常处理 推荐度
add(e) 入队:将元素添加到队列尾部。 如果队列已满,抛出 IllegalStateException
offer(e) 入队:将元素添加到队列尾部。 如果队列已满,返回 false 极高 (更安全,因为它不会抛出异常)
remove() 出队:移除并返回队列头部的元素。 如果队列为空,抛出 NoSuchElementException
poll() 出队:移除并返回队列头部的元素。 如果队列为空,返回 null 极高 (更安全,因为它不会抛出异常)
element() 查看队头:返回队列头部的元素,但不移除它。 如果队列为空,抛出 NoSuchElementException
peek() 查看队头:返回队列头部的元素,但不移除它。 如果队列为空,返回 null 极高 (更安全,因为它不会抛出异常)

代码示例:

import java.util.Deque;
import java.util.ArrayDeque;

public class QueueExample {
    public static void main(String[] args) {
        // 使用 ArrayDeque 作为队列
        Deque<String> queue = new ArrayDeque<>();

        // offer() - 入队
        queue.offer("Alice");
        queue.offer("Bob");
        queue.offer("Charlie");
        System.out.println("Queue after offers: " + queue); // [Alice, Bob, Charlie]

        // peek() - 查看队头
        System.out.println("Front element (peek): " + queue.peek()); // Alice
        System.out.println("Queue after peek: " + queue); // [Alice, Bob, Charlie] (不变)

        // poll() - 出队
        System.out.println("Polled element: " + queue.poll()); // Alice
        System.out.println("Queue after poll: " + queue); // [Bob, Charlie]
    }
}

三、总结与对比

1. 方法对比速查表
操作 栈 (Stack) 队列 (Queue)
添加元素 push(e) offer(e)
移除并返回元素 pop() poll()
查看元素 (不移除) peek() peek()
操作位置 尾部 (Tail) 尾部添加 (Tail)头部移除 (Head)
遵循原则 LIFO (后进先出) FIFO (先进先出)
2. 为什么推荐使用 Deque 而不是 Stack
  • Stack 是遗留类java.util.Stack 继承自 Vector,而 Vector 是一个同步类,所有方法都用 synchronized 修饰,在不需要线程安全的场景下会带来不必要的性能开销。
  • Deque API 更清晰、功能更强大Deque 接口专门为双端队列设计,其方法名(pushpopofferpoll)比 Stack 的方法名(pushpopaddElementremoveElement)更具表现力,并且可以轻松实现栈、队列或双端队列。
  • 现代集合框架的一部分Deque 是 Java Collections Framework (JCF) 的标准部分,遵循了统一的设计模式,使用起来更一致。
3. 选择 ArrayDeque 还是 LinkedList
  • 优先选择 ArrayDeque:在绝大多数情况下,ArrayDeque 的性能都优于 LinkedList,因为它基于数组,内存是连续的,CPU 缓存友好。
  • 考虑 LinkedList 的情况
    • 需要在队列的中间频繁进行插入或删除操作。
    • 需要一个可以实现 List 接口的队列(LinkedList 同时实现了 List 和 Deque)。

总之,记住这个黄金法则:在 Java 中,当你需要使用栈或队列时,请优先考虑使用 Deque 接口及其实现类 ArrayDeque

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐