Java栈与队列深度解析:结构、实现与应用指南
一、栈与队列核心概念对比
特性 | 栈 (Stack) | 队列 (Queue) |
---|---|---|
数据原则 | LIFO(后进先出) | FIFO(先进先出) |
核心操作 | push(入栈)、pop(出栈)、peek(查看栈顶) | offer(入队)、poll(出队)、peek(查看队首) |
典型应用 | 函数调用栈、括号匹配、撤销操作 | 任务调度、BFS算法、消息缓冲队列 |
Java实现类 | Stack (遗留类) / Deque | LinkedList / ArrayDeque / PriorityQueue |
二、栈的Java实现方案
1. 标准库实现
// 推荐使用Deque作为栈(Java官方建议)
Deque<Integer> stack = new ArrayDeque<>();
// 基本操作
stack.push(10); // 入栈
int top = stack.peek(); // 查看栈顶(不删除)
int val = stack.pop(); // 出栈
// 不推荐使用遗留Stack类(同步开销大)
Stack<String> legacyStack = new Stack<>();
2. 自定义数组栈实现
public class ArrayStack<E> {
private static final int DEFAULT_CAPACITY = 10;
private Object[] elements;
private int top = -1;
public ArrayStack() {
this(DEFAULT_CAPACITY);
}
public ArrayStack(int capacity) {
elements = new Object[capacity];
}
public void push(E item) {
if (top == elements.length - 1) {
resize();
}
elements[++top] = item;
}
public E pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
E item = (E) elements[top];
elements[top--] = null; // 帮助GC
return item;
}
private void resize() {
int newSize = elements.length * 2;
elements = Arrays.copyOf(elements, newSize);
}
}
三、队列的Java实现体系
1. 队列类型与实现类
队列类型 | 特点 | 实现类 |
---|---|---|
普通队列 | 先进先出,无优先级 | LinkedList / ArrayDeque |
优先队列 | 按优先级出队 | PriorityQueue |
阻塞队列 | 支持等待式操作 | ArrayBlockingQueue / LinkedBlockingQueue |
双端队列 | 两端都可操作 | ArrayDeque / LinkedList |
并发队列 | 线程安全 | ConcurrentLinkedQueue |
2. 队列操作对比
Queue<String> queue = new LinkedList<>();
// 添加元素
queue.offer("A"); // 推荐(返回boolean)
queue.add("B"); // 可能抛异常
// 移除元素
String head = queue.poll(); // 返回null为空
String elem = queue.remove(); // 空队列抛异常
// 查看队首
String peek = queue.peek(); // 不删除元素
四、核心数据结构实现原理
1. 栈的链表实现
public class LinkedStack<E> {
private static class Node<E> {
E item;
Node<E> next;
Node(E item, Node<E> next) {
this.item = item;
this.next = next;
}
}
private Node<E> top;
private int size;
public void push(E item) {
top = new Node<>(item, top);
size++;
}
public E pop() {
if (top == null) throw new EmptyStackException();
E item = top.item;
top = top.next;
size--;
return item;
}
}
2. 循环队列实现(解决假溢出)
public class CircularQueue<E> {
private final Object[] elements;
private int front; // 队首指针
private int rear; // 队尾指针
private int count; // 元素计数
public CircularQueue(int capacity) {
elements = new Object[capacity];
}
public boolean offer(E e) {
if (count == elements.length) return false;
elements[rear] = e;
rear = (rear + 1) % elements.length;
count++;
return true;
}
public E poll() {
if (count == 0) return null;
E e = (E) elements[front];
elements[front] = null;
front = (front + 1) % elements.length;
count--;
return e;
}
}
五、高级应用场景
1. 栈的应用:括号匹配检查
public static boolean isBalanced(String expression) {
Deque<Character> stack = new ArrayDeque<>();
for (char c : expression.toCharArray()) {
if (c == '(' || c == '[' || c == '{') {
stack.push(c);
} else {
if (stack.isEmpty()) return false;
char top = stack.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return stack.isEmpty();
}
2. 队列的应用:生产者-消费者模型
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(10);
// 生产者线程
new Thread(() -> {
while (true) {
Task task = generateTask();
try {
queue.put(task); // 阻塞直到有空间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
// 消费者线程
new Thread(() -> {
while (true) {
try {
Task task = queue.take(); // 阻塞直到有元素
processTask(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}).start();
六、性能对比与选型建议
1. 不同实现的性能对比
操作 | ArrayDeque | LinkedList | PriorityQueue |
---|---|---|---|
插入/删除 | O(1) | O(1) | O(log n) |
随机访问 | O(1) | O(n) | O(n) |
内存占用 | 连续空间更优 | 节点额外开销 | 数组堆结构 |
2. 选型决策树
是否需要优先级处理? ├── 是 → 使用PriorityQueue └── 否 → 需要线程安全? ├── 是 → 使用ConcurrentLinkedQueue或BlockingQueue └── 否 → 预估数据量? ├── 小 → LinkedList └── 大 → ArrayDeque
七、常见问题解决方案
1. 实现最小栈(O(1)获取最小值)
class MinStack {
private Deque<Integer> stack = new ArrayDeque<>();
private Deque<Integer> minStack = new ArrayDeque<>();
public void push(int x) {
stack.push(x);
if (minStack.isEmpty() || x <= minStack.peek()) {
minStack.push(x);
}
}
public void pop() {
if (stack.pop().equals(minStack.peek())) {
minStack.pop();
}
}
public int getMin() {
return minStack.peek();
}
}
2. 用队列实现栈
class MyStack {
Queue<Integer> queue = new LinkedList<>();
public void push(int x) {
queue.offer(x);
// 将前n-1个元素重新入队
for (int i = 1; i < queue.size(); i++) {
queue.offer(queue.poll());
}
}
public int pop() {
return queue.poll();
}
}
八、Java 8+新特性应用
1. Stream操作队列
Queue<Integer> queue = new LinkedList<>(Arrays.asList(3,1,4,1,5));
// 过滤并收集
List<Integer> filtered = queue.stream()
.filter(n -> n > 2)
.collect(Collectors.toList());
// 并行处理(注意线程安全)
ConcurrentLinkedQueue<Integer> safeQueue = new ConcurrentLinkedQueue<>(queue);
safeQueue.parallelStream()
.map(n -> n * 2)
.forEach(System.out::println);
2. Lambda表达式简化操作
PriorityQueue<String> pq = new PriorityQueue<>(
(a, b) -> b.length() - a.length() // 自定义比较器
);
pq.offer("Java");
pq.offer("Python");
pq.offer("C++");
while (!pq.isEmpty()) {
System.out.println(pq.poll());
// 输出:Python, Java, C++
}
九、最佳实践总结
-
栈的选择原则:
-
单线程环境优先使用
ArrayDeque
-
需要同步时使用
Collections.synchronizedDeque()
-
避免使用遗留的
Stack
类
-
-
队列的选择原则:
-
高并发场景使用
ConcurrentLinkedQueue
-
需要阻塞功能选择
BlockingQueue
实现 -
优先级处理使用
PriorityQueue
-
-
通用建议:
-
预估数据规模选择底层存储结构
-
注意边界条件(空栈/空队列操作)
-
合理使用容量限制防止内存溢出
-
性能口诀:
-
数组实现随机访问快
-
链表实现增删效率高
-
优先队列按需排序
-
并发场景选择线程安全实现
通过深入理解栈和队列的特性,开发者可以更高效地解决算法问题和系统设计挑战。