LinkedList 基於雙向鏈表實現 List 與 Deque 接口,擅長頻繁插入刪除、頭尾操作。本文深入剖析其節點結構、核心方法實現、迭代器機制及與 ArrayList 的選型邊界。
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;
}
}
關鍵字段:first 指向頭節點,last 指向尾節點,size 記錄元素數量,modCount 記錄修改次數(用於 fail-fast)。
| 構造方法 | 行為 |
|---|---|
LinkedList() | 創建空鏈表,first=last=null, size=0 |
LinkedList(Collection<? extends E> c) | 按集合迭代順序依次 linkLast 添加元素 |
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
時間複雜度:O(1),直接操作尾指針。
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) first = next;
else { prev.next = next; x.prev = null; }
if (next == null) last = prev;
else { next.prev = prev; x.next = null; }
x.item = null; // 助 GC
size--;
modCount++;
return element;
}
時間複雜度:O(1),給定節點直接斷開鏈接。
public boolean add(E e) { linkLast(e); return true; }
public void addLast(E e) { linkLast(e); }
public void addFirst(E e) { linkFirst(e); }
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// 優化:index < size/2 從頭遍歷,否則從尾遍歷
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),雖有雙向優化,但仍需遍歷。
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next)
if (x.item == null) { unlink(x); return true; }
} else {
for (Node<E> x = first; x != null; x = x.next)
if (o.equals(x.item)) { unlink(x); return true; }
}
return false;
}
時間複雜度:O(n),需線性查找節點。
| 方法 | 對應鏈表操作 | 時間複雜度 |
|---|---|---|
| push(E e) / addFirst(E e) | linkFirst | O(1) |
| pop() / removeFirst() | unlinkFirst | O(1) |
| addLast(E e) / offer(E e) | linkLast | O(1) |
| removeLast() / pollLast() | unlinkLast | O(1) |
| peek() / peekFirst() | first.item | O(1) |
| peekLast() | last.item | O(1) |
LinkedList.listIterator(index) 返回 ListItr 內部類,維護 lastReturned、next、nextIndex,支持雙向遍歷、add/set/remove 原地修改,modCount 機制保證 fail-fast。
| 操作 | ArrayList | LinkedList |
|---|---|---|
| get/set 隨機訪問 | O(1) ⭐ | O(n) |
| add/remove 尾部 | O(1) 攤還 | O(1) ⭐ |
| add/remove 頭部 | O(n) | O(1) ⭐ |
| add/remove 中間(給定索引) | O(n) 移動元素 | O(n) 查找 + O(1) 斷開 |
| 內存開銷 | 低(緊湊數組) | 高(Node 對象 + 2引用) |
| 緩存局部性 | 優(連續內存) | 差(離散節點) |