java.util.Stack 是 JDK 1.0 即提供的經典後進先出(LIFO)棧實現。雖然官方文檔建議優先使用 ArrayDeque,但在維護遺留代碼、理解棧原理、特定場景下仍具參考價值。本文從繼承體系、源碼實現、API 詳解到實戰示例全方位剖析。
一、類繼承與定位
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.Vector<E>
java.util.Stack<E>
public class Stack<E> extends Vector<E> { }
- 繼承
Vector → 底層動態數組存儲,而非鏈表。
- 繼承所有
Vector 方法(含同步機制),並額外提供 5 個棧專用 API。
- 線程安全(方法級
synchronized),但同步粒度粗、性能一般。
二、棧專用 API 速查
| 方法 | 簽名 | 語義 | 異常情況 |
| push | E push(E item) | 壓入棧頂(數組末尾),返回 item | 無 |
| pop | synchronized E pop() | 彈出棧頂並移除,返回元素 | 空棧拋 EmptyStackException |
| peek | synchronized E peek() | 查看棧頂不移除,返回元素 | 空棧拋 EmptyStackException |
| empty | boolean empty() | 判斷棧是否為空 | 無 |
| search | synchronized int search(Object o) | 從棧底向頂查找,返回 1-based 位置 | 未找到返回 -1 |
三、源碼核心邏輯(JDK 8)
public class Stack<E> extends Vector<E> {
private static final long serialVersionUID = 1224463164541339165L;
public Stack() { } // 唯一構造函數,委託 Vector()
// 壓棧:委託 Vector.addElement() → 數組末尾追加
public E push(E item) {
addElement(item);
return item;
}
// 彈棧:同步獲取棧頂 → 同步移除最後一個元素
public synchronized E pop() {
E obj;
int len = size();
obj = peek(); // 拋空棧異常
removeElementAt(len - 1); // 刪除數組最後一位
return obj;
}
// 查看棧頂:同步返回數組最後一個元素
public synchronized E peek() {
int len = size();
if (len == 0) throw new EmptyStackException();
return elementAt(len - 1);
}
public boolean empty() {
return size() == 0;
}
// 搜索:從棧底向頂,返回 1-based 索引
public synchronized int search(Object o) {
int i = lastIndexOf(o); // Vector 從尾向頭找
if (i >= 0) return size() - i;
return -1;
}
}
關鍵點總結
- 數組末尾即棧頂:
push = addElement(尾部追加),pop/peek = 操作 elementData[size-1]。
- 同步粒度:
pop/peek/search 加 synchronized,push/empty 依賴父類同步方法。
- search 語義特殊:返回「距離棧頂的 1-based 距離」,非標準索引。
四、實戰示例
import java.util.*;
public class StackDemo {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
// 壓入 1~5
for (int i = 1; i <= 5; i++) stack.push(String.valueOf(i));
System.out.println("初始棧(底→頂): " + stack); // [1, 2, 3, 4, 5]
// 搜索位置
System.out.println("元素 '2' 位置: " + stack.search("2")); // 4 (頂→底第4個)
// 彈出棧頂
System.out.println("pop: " + stack.pop()); // 5
printStack(stack);
// 查看棧頂
System.out.println("peek: " + stack.peek()); // 4
printStack(stack);
// 兩種遍歷方式
System.out.println("\n--- 隨機訪問遍歷 ---");
iteratorRandomAccess(stack);
System.out.println("\n--- Iterator 遍歷 ---");
iteratorIterator(stack);
}
static void printStack(Stack<String> s) {
System.out.print("當前棧: ");
for (int i = 0; i < s.size(); i++) System.out.print(s.get(i) + " ");
System.out.println();
}
static void iteratorRandomAccess(List<String> list) {
for (int i = 0; i < list.size(); i++) System.out.print(list.get(i) + " ");
System.out.println();
}
static void iteratorIterator(List<String> list) {
for (Iterator<String> it = list.iterator(); it.hasNext(); )
System.out.print(it.next() + " ");
System.out.println();
}
}
五、Stack vs ArrayDeque:為何官方推薦後者
| 維度 | Stack | ArrayDeque |
| 底層結構 | 動態數組(繼承 Vector) | 循環數組(雙端隊列) |
| 線程安全 | 同步方法(粗粒度鎖) | 非線程安全(高性能) |
| 擴容機制 | 1.5 倍/2 倍(Vector 策略) | 2 倍,無同步開銷 |
| 功能豐富度 | 僅 LIFO 棧 | 雙端隊列、棧、隊列通用 |
| Null 元素 | 允許 | 不允許 |
| 推薦場景 | 遺留代碼、教學演示 | 所有新代碼棧/隊列需求 |
最佳實踐:
// 新代碼首選
Deque<String> stack = new ArrayDeque<>();
stack.push("a");
stack.pop();
stack.peek();
- 單線程高性能棧 →
ArrayDeque。
- 多線程併發棧 →
ConcurrentLinkedDeque 或加鎖 ArrayDeque。
- 僅維護舊代碼時接觸
Stack,理解其同步與數組特性即可。