`ArrayList` 與 `LinkedList` 是 `List` 接口最核心的兩種實現,底層分別基於**動態陣列**與**雙向鏈表**。本文從**源碼級字段結構、核心方法實現、擴容/縮容策略、記憶體佈局、JMH 效能基準、實戰選型決策樹**六個維度深度解析,助你在面試與架構設計中做出精準選擇。
| 屬性 | ArrayList | LinkedList |
|---|---|---|
| 底層結構 | `transient Object[] elementData` | `transient Node `private static class Node |
| 大小字段 | `int size` | `int size` |
| 默認容量 | `DEFAULT_CAPACITY = 10` | 無容量概念 (按需分配節點) |
| 空陣列單例 | `EMPTY_ELEMENTDATA` / `DEFAULTCAPACITY_EMPTY_ELEMENTDATA` | 無 |
| 修改計數 | `protected transient int modCount` (fail-fast) | `protected transient int modCount` |
| 實現介面 | `List | `List |
public boolean add(E e) {
modCount++;
add(e, elementData, size); // 內部靜態方法
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow(); // 擴容
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
return Arrays.copyOf(elementData, newCapacity);
} else {
return new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
// ArrayList: O(1) 直接下標
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) { return (E) elementData[index]; }
// LinkedList: O(min(index, size-index)) 雙向遍歷
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node node(int index) {
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++) x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--) x = x.prev;
return x;
}
}
| 操作 | ArrayList | LinkedList |
|---|---|---|
| 插入 | `System.arraycopy` 後移 O(n-index) + 賦值 | 遍歷到前驅節點 O(index) + 修改指針 O(1) |
| 刪除 | `System.arraycopy` 前移 O(n-index) | 遍歷到節點 O(index) + 修改指針 O(1) |
| 記憶體 | 陣列連續、GC 友好 | 產生大量短命節點對象、GC 壓力大 |
// ArrayList: 1 個陣列對象 + 元素引用 (連續)
[ArrayList Object] → [Object[] elementData] → [ref0, ref1, ref2, ...]
// 100萬 Integer: ~4MB 陣列 + 100萬 Integer 對象 (分散)
// LinkedList: 100萬 Node 對象 + 100萬 Integer 對象
[LinkedList Object] → first → [Node: item=Integer, next, prev] → [Node] → ...
// 額外 ~24 bytes/節點 (對象頭 + 2引用) → 100萬約 24MB 額外開銷
Benchmark Mode Cnt Score Error Units
ArrayList.add_尾部 avgt 10 12.3 ± 0.4 ms/op ← 最快
LinkedList.add_尾部 avgt 10 45.6 ± 1.2 ms/op ← 節點分配
ArrayList.get_隨機訪問 avgt 10 0.8 ± 0.1 ns/op ← O(1)
LinkedList.get_隨機訪問 avgt 10 850.0 ± 12.0 ns/op ← 遍歷
ArrayList.add_中間 (index=size/2) avgt 10 210.0 ± 5.0 ms/op ← arraycopy
LinkedList.add_中間 (index=size/2) avgt 10 380.0 ± 8.0 ms/op ← 遍歷+分配
ArrayList.remove_中間 avgt 10 190.0 ± 4.0 ms/op
LinkedList.remove_中間 avgt 10 350.0 ± 7.0 ms/op
ArrayList.遍歷_forEach avgt 10 15.0 ± 0.3 ms/op ← 緩存友好
LinkedList.遍歷_forEach avgt 10 42.0 ± 1.0 ms/op ← 緩存未命中
LinkedList.addFirst/addLast (Deque) avgt 10 5.2 ± 0.2 ms/op ← 極快
主要操作模式?
├─ 隨機訪問 (get/set)、尾部增刪、遍歷多 → **ArrayList** ← 90% 場景首選
├─ 頻繁首尾增刪 (棧/隊列/雙端隊列) → **ArrayDeque** (比 LinkedList 更快、無節點開銷)
├─ 頻繁中間增刪、極少隨機訪問 → 考慮 LinkedList (極罕見)
│ └─ 實測發現 ArrayList 仍更快 (arraycopy 極優化) → 仍用 ArrayList
└─ 需 List + Deque 雙重語義 → LinkedList (實現了 Deque)
但推薦:ArrayList + ArrayDeque 組合
💡 核心心法:默認用 ArrayList,佇列用 ArrayDeque,極少數雙向遍歷+首尾操作用 LinkedList,並發用並發容器。`LinkedList` 幾乎沒有獨佔優勢場景,`ArrayDeque` 完美替代其 Deque 能力且效能更優。