LinkedList 實作 List、Queue、Deque 三大介面,底層為雙向鏈表。適用於頻繁頭尾增刪、作為堆疊/佇列/雙端佇列;不適合隨機存取(get(index) 為 O(n))。
LinkedList → AbstractSequentialList (基於迭代器實作隨機存取) → AbstractList → AbstractCollection → List, Queue, Deque, Cloneable, Serializable
| 方法 | 行為 | 失敗處理 | 推薦度 |
|---|---|---|---|
add(e) | 入隊尾部 | 拋 IllegalStateException | ⭐ |
offer(e) | 入隊尾部 | 回傳 false | ⭐⭐⭐ |
remove() | 出隊頭部 | 拋 NoSuchElementException | ⭐ |
poll() | 出隊頭部 | 回傳 null | ⭐⭐⭐ |
element() | 查看隊頭 | 拋異常 | ⭐ |
peek() | 查看隊頭 | 回傳 null | ⭐⭐⭐ |
最佳實踐:優先使用 offer/poll/peek,避免異常控制流程。注意 LinkedList 允許 null 元素,佇列語義下不應插入 null(無法區分「隊列空」與「隊頭為 null」)。
繼承 Queue,新增頭尾對稱操作:
addFirst/offerFirst、addLast/offerLastremoveFirst/pollFirst、removeLast/pollLastgetFirst/peekFirst、getLast/peekLastpush(e) ≡ addFirst(e)、pop() ≡ removeFirst()descendingIterator()private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element; this.next = next; this.prev = prev;
}
}
關鍵欄位:transient Node<E> first(頭節點)、last(尾節點)、size、modCount(快速失效計數)。
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null; f.next = null; // 協助 GC
first = next;
if (next == null) last = null;
else next.prev = null;
size--; modCount++;
return element;
}
O(1) 時間,僅調整頭指標與 GC 清理。
Node<E> node(int index) {
if (index < (size >> 1)) { // 前半段從頭遍歷
Node<E> x = first;
for (int i = 0; i < index; i++) x = x.next;
return x;
} else { // 後半段從尾遍歷
Node<E> x = last;
for (int i = size - 1; i > index; i--) x = x.prev;
return x;
}
}
雙向遍歷優化,但仍為 O(n),嚴禁在大量資料場景下頻繁呼叫 get(index)。
// 10,000 筆資料,各存取 10,000 次 LinkedList: 56 ms ArrayList: 1 ms
結論:隨機存取 ArrayList 完勝(連續記憶體 + CPU 快取友善)。LinkedList 僅在頭尾增刪、中間插入/刪除已知節點 時具優勢。
ArrayListArrayDeque(陣列實作,效能優於 LinkedList)LinkedListCollections.synchronizedList 或 ConcurrentLinkedQueuemodCount 機制:迭代過程中結構性修改會拋 ConcurrentModificationException。toArray() 回傳 Object[],需強轉或用 toArray(new String[0])。transient 節點會重建,writeObject/readObject 手動處理。