java.util.LinkedList 是基於雙向鏈表實現的 List 與 Deque 雙重身份集合。它在頻繁頭尾插刪、棧/隊列場景下性能優異,但隨機訪問極弱。本文從源碼結構、核心 API、棧隊列用法、遍歷效率實測五個維度全方位剖析。
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable { }
AbstractSequentialList → 獲取基於索引的 get/set/add/remove 默認實現(內部委託鏈表遍歷)。List:支持索引操作、遍歷。Deque:雙端隊列語義,提供 addFirst/Last、pollFirst/Last、push/pop/peek 等豐富 API。private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
transient Entry<E> header = new Entry<>(null, null, null); // 哨兵節點,不存數據
transient int size = 0;
header 形成循環雙向鏈表:header.next 指向首節點,header.previous 指向尾節點,空表時指向自身。private Entry<E> entry(int index) {
if (index < 0 || index >= size) throw new IndexOutOfBoundsException(...);
Entry<E> e = header;
if (index < (size >> 1)) { // 前半段:從頭向後
for (int i = 0; i <= index; i++) e = e.next;
} else { // 後半段:從尾向前
for (int i = size; i > index; i--) e = e.previous;
}
return e;
}
雖有優化,但隨機訪問仍屬 O(n) 線性級,嚴禁大量隨機訪問。
addBefore(e, entry)private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
四步指針操作完成插入,O(1)(不含定位)。addFirst/addLast/push/offer 皆委託此法。
remove(Entry<E> e)private E remove(Entry<E> e) {
if (e == header) throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null; // 釋放引用助 GC
size--;
modCount++;
return result;
}
ListItr 內部類實現 ListIterator,維護 expectedModCount,每次操作前檢查 modCount,異常拋 ConcurrentModificationException。支持雙向遍歷、add/set/remove 三大修改操作。
| 操作 | 首部(頭) | 尾部(尾) | 異常風格 | 特殊值風格 |
|---|---|---|---|---|
| 插入 | addFirst(e) | addLast(e) | 拋異常 | offerFirst(e)/offerLast(e) 返回 boolean |
| 移除 | removeFirst() | removeLast() | 拋異常 | pollFirst()/pollLast() 返回 null |
| 檢查 | getFirst() | getLast() | 拋異常 | peekFirst()/peekLast() 返回 null |
push(e) ≡ addFirst(e)
pop() ≡ removeFirst()
peek() ≡ peekFirst()
add(e) ≡ addLast(e)
offer(e) ≡ offerLast(e)
remove() ≡ removeFirst()
poll() ≡ pollFirst()
element() ≡ getFirst()
peek() ≡ peekFirst()
iteratorLinkedListThruIterator:8 ms // Iterator 只讀遍歷
iteratorThroughFor2:5 ms // for-each (內部用 Iterator) ★推薦只讀
iteratorThroughPollFirst:8 ms // pollFirst() 邊遍歷邊刪除
iteratorThroughPollLast:6 ms // pollLast() 邊遍歷邊刪除
iteratorThroughRemoveFirst:2 ms // removeFirst() 邊遍歷邊刪除 ★最快但銷毀數據
iteratorThroughRemoveLast:2 ms // removeLast() 邊遍歷邊刪除
iteratorLinkedListThruForeach:3724 ms // 下標隨機訪問 list.get(i) ★極慢,禁用!
黃金法則:
- 只讀遍歷 →
for (E e : list)或list.forEach()。- 需邊遍歷邊刪除 →
pollFirst()/pollLast()(返回 null 終止)或Iterator.remove()。- 絕對禁止 使用
for (int i=0; i。
import java.util.*;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
// 基礎增刪改查
list.add("1"); list.add("2"); list.add("3");
list.add(1, "4"); // 索引 1 插入 → [1, 4, 2, 3]
System.out.println("基礎操作後: " + list);
// Deque 首尾操作
System.out.println("\n--- addFirst/removeFirst/getFirst ---");
list.addFirst("10"); System.out.println(list);
System.out.println("removeFirst: " + list.removeFirst()); System.out.println(list);
System.out.println("getFirst: " + list.getFirst());
System.out.println("\n--- offerFirst/pollFirst/peekFirst ---");
list.offerFirst("10"); System.out.println(list);
System.out.println("pollFirst: " + list.pollFirst()); System.out.println(list);
System.out.println("peekFirst: " + list.peekFirst());
// 棧模式
System.out.println("\n--- LIFO Stack 模式 ---");
LinkedList<String> stack = new LinkedList<>();
stack.push("1"); stack.push("2"); stack.push("3"); stack.push("4");
System.out.println("stack: " + stack);
System.out.println("pop: " + stack.pop());
System.out.println("peek: " + stack.peek());
System.out.println("stack: " + stack);
// 隊列模式
System.out.println("\n--- FIFO Queue 模式 ---");
LinkedList<String> queue = new LinkedList<>();
queue.add("10"); queue.add("20"); queue.add("30"); queue.add("40");
System.out.println("queue: " + queue);
System.out.println("remove: " + queue.remove());
System.out.println("element: " + queue.element());
System.out.println("queue: " + queue);
// 轉數組
String[] arr = list.toArray(new String[0]);
System.out.println("\ntoArray: " + Arrays.toString(arr));
// 清空
list.clear();
System.out.println("isEmpty: " + list.isEmpty());
}
}
| 場景 | 推薦 | 理由 |
|---|---|---|
| 高頻頭尾插刪、棧/隊列 | LinkedList / ArrayDeque | O(1) 指針操作,ArrayDeque 數組結構常數因子更小 |
| 高頻隨機訪問 | ArrayList | O(1) 下標直達 |
| 多線程併發 | ConcurrentLinkedDeque / 加鎖 ArrayDeque | LinkedList 非線程安全 |
| 內存敏感、大量小對象 | ArrayList / ArrayDeque | LinkedList 每節點 3 引用開銷大 |
ArrayDeque(非線程安全)或 ConcurrentLinkedDeque(併發)。List 索引操作又需頻繁頭尾插刪 → 權衡或分離關注點(用兩個集合)。