栈(Stack)是遵循后进先出(LIFO, Last In First Out) 规则的线性数据结构,Java中有两种主流的栈实现方式:
- 遗留类:
java.util.Stack(继承自Vector,线程安全但效率低,不推荐使用); - 推荐方式:
java.util.Deque(双端队列)接口的实现类(如LinkedList、ArrayDeque),功能更完善、效率更高,是官方推荐的栈替代方案。
一、栈的核心操作(按常用程度排序)
以下先统一说明核心操作的含义,再分别给出Stack和Deque的实现代码,重点讲解推荐的Deque用法。
| 操作名称 | 功能描述 | Stack方法 | Deque推荐方法(栈模式) | 栈空时行为 |
|---|---|---|---|---|
| 入栈 | 将元素压入栈顶 | push(E e) | push(E e) / offerFirst(E e) | 无异常(容量足够时) |
| 出栈 | 移除并返回栈顶元素 | pop() | pop() / pollFirst() | pop抛异常;pollFirst返回null |
| 查看栈顶 | 返回栈顶元素但不移除 | peek() | peek() / peekFirst() | peek抛异常;peekFirst返回null |
| 判空 | 判断栈是否为空 | isEmpty() | isEmpty() | - |
| 获取大小 | 返回栈中元素个数 | size() | size() | - |
| 清空栈 | 移除栈中所有元素 | clear() | clear() | - |
| 检查元素 | 判断栈中是否包含指定元素 | contains(Object o) | contains(Object o) | - |
二、代码示例(推荐Deque实现)
1. 基础操作示例(Deque + ArrayDeque)
ArrayDeque是Deque的数组实现,效率高于LinkedList,是栈的首选实现:
java
import java.util.ArrayDeque;
import java.util.Deque;
public class StackOperationsDemo {
public static void main(String[] args) {
// 1. 初始化栈(Deque作为栈使用,推荐ArrayDeque)
Deque<Integer> stack = new ArrayDeque<>();
// 2. 入栈(push)
stack.push(1); // 栈:[1]
stack.push(2); // 栈:[2, 1]
stack.push(3); // 栈:[3, 2, 1]
System.out.println("入栈后栈内容:" + stack); // 输出 [3, 2, 1](Deque打印顺序是头到尾,栈顶是头)
// 3. 查看栈顶(peek)
Integer topElement = stack.peek();
System.out.println("栈顶元素:" + topElement); // 输出 3(栈顶不变)
System.out.println("查看栈顶后栈内容:" + stack); // 输出 [3, 2, 1]
// 4. 出栈(pop)
Integer popElement = stack.pop();
System.out.println("出栈元素:" + popElement); // 输出 3
System.out.println("出栈后栈内容:" + stack); // 输出 [2, 1]
// 5. 判空(isEmpty)
boolean isEmpty = stack.isEmpty();
System.out.println("栈是否为空:" + isEmpty); // 输出 false
// 6. 获取栈大小(size)
int size = stack.size();
System.out.println("栈大小:" + size); // 输出 2
// 7. 检查元素是否存在(contains)
boolean contains = stack.contains(1);
System.out.println("栈是否包含1:" + contains); // 输出 true
// 8. 清空栈(clear)
stack.clear();
System.out.println("清空后栈是否为空:" + stack.isEmpty()); // 输出 true
// 9. 安全出栈/查看(避免空栈异常)
// pollFirst:栈空返回null,不抛异常(替代pop)
Integer safePop = stack.pollFirst();
System.out.println("空栈安全出栈:" + safePop); // 输出 null
// peekFirst:栈空返回null,不抛异常(替代peek)
Integer safePeek = stack.peekFirst();
System.out.println("空栈安全查看栈顶:" + safePeek); // 输出 null
}
}
2. 遗留类Stack的示例(了解即可,不推荐)
java.util.Stack是遗留类,继承自Vector,线程安全但性能差,且方法命名和Deque一致,示例如下:
java
import java.util.Stack;
public class LegacyStackDemo {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
// 入栈
stack.push("A");
stack.push("B");
// 查看栈顶
System.out.println("栈顶:" + stack.peek()); // B
// 出栈
System.out.println("出栈:" + stack.pop()); // B
// 判空
System.out.println("是否为空:" + stack.isEmpty()); // false
// 栈空时pop/peek会抛EmptyStackException
stack.pop(); // 栈变为空
// stack.pop(); // 抛EmptyStackException
}
}
三、关键注意事项
- 空栈异常 :
Stack.pop()/Stack.peek()、Deque.pop()/Deque.peek()在栈空时会抛出EmptyStackException/NoSuchElementException,推荐使用Deque.pollFirst()/Deque.peekFirst()(返回null,更安全); - 线程安全 :
Stack是线程安全的(基于Vector的同步方法),但效率低;ArrayDeque/LinkedList非线程安全,若需线程安全的栈,可使用Collections.synchronizedDeque(new ArrayDeque<>()); - Deque作为栈的规范 :Deque是双端队列,作为栈使用时,仅使用
push()/pop()/peek()(等价于offerFirst()/removeFirst()/peekFirst()),避免使用队列相关方法(如add()/remove()),保证栈的LIFO规则。
总结
- Java栈核心操作包括入栈(push) 、出栈(pop) 、查看栈顶(peek) 、判空(isEmpty) 、获取大小(size) 、清空(clear) 等;
- 不推荐使用遗留类
java.util.Stack,优先选择Deque接口(ArrayDeque/LinkedList)实现栈功能; - 空栈操作时,优先使用
pollFirst()/peekFirst()替代pop()/peek(),避免空指针异常,提升代码健壮性。