PriorityQueue 是 Java 集合框架中基於最小堆實現的無界優先隊列。不同於普通隊列的 FIFO,它保證每次出隊(poll/peek)獲得隊列中最小元素(按自然順序或自定義 Comparator)。本文從數據結構原理、源碼核心方法、動態圖解、性能複雜度、使用陷阱五個維度徹底講透。
對於 0-based 索引數組 queue[],節點 i 的:
left(i) = 2*i + 1right(i) = 2*i + 2parent(i) = (i-1) >>> 1 (無符號右移 1 位,等價除 2 取整)(size >>> 1) - 1這種映射讓樹形結構在數組中無指針開銷、緩存友好、隨機訪問 O(1)。
源碼 private void heapify():
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
siftDown(下沉)。

public boolean offer(E e) {
if (e == null) throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length) grow(i + 1); // 擴容 1.5倍 + 1
size = i + 1;
if (i == 0) queue[0] = e;
else siftUp(i, e); // 核心:上浮調整
return true;
}
private void siftUp(int k, E x) {
if (comparator != null) siftUpUsingComparator(k, x);
else siftUpComparable(k, x);
}
// 自然順序上浮
private void siftUpComparable(int k, E x) {
Comparable super E> key = (Comparable super E>) x;
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0) break; // 父節點 ≤ 當前,滿足最小堆,停止
queue[k] = e; // 父節點下沉
k = parent;
}
queue[k] = key; // 最終位置放入
}

public E poll() {
if (size == 0) return null;
int s = --size;
modCount++;
E result = (E) queue[0];
E x = (E) queue[s];
queue[s] = null;
if (s != 0) siftDown(0, x); // 將尾元素移到根,下沉
return result;
}
private void siftDown(int k, E x) {
if (comparator != null) siftDownUsingComparator(k, x);
else siftDownComparable(k, x);
}
// 自然順序下沉
private void siftDownComparable(int k, E x) {
Comparable super E> key = (Comparable super E>) x;
int half = size >>> 1; // 非葉子節點邊界
while (k < half) { // 只有非葉子節點才可能下沉
int child = (k << 1) + 1; // 左子節點
Object c = queue[child];
int right = child + 1;
// 選兩個子節點中較小者
if (right < size && ((Comparable super E>) c).compareTo((E) queue[right]) > 0)
c = queue[child = right];
if (key.compareTo((E) c) <= 0) break; // 當前 ≤ 子節點最小,停止
queue[k] = c; // 子節點上移
k = child;
}
queue[k] = key;
}

private E removeAt(int i) {
// ... 邊界檢查 ...
int s = --size;
if (s == i) queue[i] = null; // 刪除尾部,直接置 null
else {
E moved = (E) queue[s];
queue[s] = null;
siftDown(i, moved); // 先嘗試下沉
if (queue[i] == moved) { // 位置未變,說明需上浮
siftUp(i, moved);
}
}
return null;
}

| 操作 | 時間複雜度 | 說明 |
|---|---|---|
| add / offer | O(log n) | 上浮高度 ≤ 樹高 log₂n |
| poll / remove | O(log n) | 下沉高度 ≤ 樹高 |
| peek / element | O(1) | 直接返回 queue[0] |
| contains / remove(Object) | O(n) | 線性掃描數組,需 equals 比較 |
| 建堆 | O(n) | heapify() 批量構建 |
PriorityBlockingQueue(內部用 ReentrantLock + Condition)。offer(null) 拋 NPE,因比較器無法處理 null。iterator() 返回數組順序遍歷,不保證優先級順序。需有序輸出請 Arrays.sort(queue.toArray()) 或反複 poll()。grow(),大量數據預估初始容量避免頻繁擴容拷貝。Comparator super E>,覆蓋自然順序,注意處理 null 與一致性。Serializable,寫入 size、comparator、queue 數組內容。ScheduledThreadPoolExecutor 內部使用 DelayedWorkQueue 繼承自 PriorityQueue)。PriorityQueue 以數組存儲完全二叉樹、siftUp/siftDown 維護最小堆性質,實現了O(log n) 入隊出隊、O(1) 取最小的高性能優先隊列。掌握其建堆 O(n) 線性算法、雙向調整移除邏輯、非線程安全與迭代器無序等核心細節,是面試高頻考點與高併發任務調度系統設計的基石。下一期我們將剖析 ArrayDeque 雙端隊列與 LinkedList 的異同,敬請期待!