List 是 Java 集合框架中有序、可重複的集合介面。三大主流實作——ArrayList、LinkedList、Vector——在底層資料結構、效能特性、執行緒安全性上有本質差異。本文系統對比三者,提供選型決策樹與最佳實務。
| 特性 | ArrayList | LinkedList | Vector |
|---|---|---|---|
| 底層結構 | 動態陣列 | 雙向鏈結串列 | 動態陣列 |
| 執行緒安全 | 否 | 否 | 是 (方法同步) |
| 擴容機制 | 1.5 倍 | 無 (節點動態分配) | 2 倍 |
| 隨機存取 get(index) | O(1) ⭐ | O(n) - 需遍歷 | O(1) |
| 頭尾增刪 | O(n) - 需搬移 | O(1) ⭐ | O(n) |
| 中間增刪 | O(n) - 需搬移 | O(n) - 需遍歷到位置 | O(n) |
| 記憶體開銷 | 低 (連續記憶體) | 高 (每節點 3 個參考) | 低 |
| 實作介面 | List, RandomAccess, Cloneable, Serializable | List, Deque, Cloneable, Serializable | List, RandomAccess, Cloneable, Serializable |
// 位置操作
void add(int index, E element)
boolean addAll(int index, Collection extends E> c)
E get(int index)
E set(int index, E element)
E remove(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
List subList(int fromIndex, int toIndex) // 視圖,非複製
ListIterator listIterator()
ListIterator listIterator(int index)
List list = new ArrayList<>();
list.add("Java"); // [Java]
list.add("Python"); // [Java, Python]
list.add(1, "Go"); // [Java, Go, Python] - 指定索引插入
System.out.println(list.get(1)); // Go
list.set(1, "Rust"); // [Java, Rust, Python] - 替換
list.remove(0); // [Rust, Python] - 移除索引 0
// 批次操作
List more = List.of("C++", "JS");
list.addAll(0, more); // [C++, JS, Rust, Python]
// 子列表視圖(修改視圖會影響原列表)
List sub = list.subList(1, 3); // [JS, Rust]
sub.set(0, "TypeScript"); // 原列表也變: [C++, TypeScript, Rust, Python]
Deque deque = new LinkedList<>();
deque.addFirst("Head"); // 等同 push
deque.addLast("Tail"); // 等同 add/offer
deque.offerFirst("NewHead");
deque.offerLast("NewTail");
System.out.println(deque.peekFirst()); // 查看頭不移除
System.out.println(deque.pollFirst()); // 取出並移除頭
System.out.println(deque.pollLast()); // 取出並移除尾
// JDK 1.6+ 新增方法(無例外版本)
deque.offerFirst(x); // 等同 addFirst 但不拋例外
deque.peekFirst(); // 等同 getFirst 但空回傳 null
deque.pollFirst(); // 等同 removeFirst 但空回傳 null
ListIterator 繼承 Iterator,新增反向遍歷、索引查詢、元素修改/新增:
List list = new ArrayList<>(List.of("A", "B", "C", "D"));
ListIterator it = list.listIterator();
while (it.hasNext()) {
String val = it.next();
if ("B".equals(val)) {
it.set("B-Modified"); // 修改剛讀取的元素
it.add("Inserted"); // 在游標前插入
}
}
// 結果: [A, B-Modified, Inserted, C, D]
// 反向遍歷
while (it.hasPrevious()) {
System.out.println(it.previousIndex() + ": " + it.previous());
}
Vector 所有方法 synchronized,效能差,已被淘汰。現代替代方案:
// 1. Collections.synchronizedList (鎖住整個列表)
List syncList = Collections.synchronizedList(new ArrayList<>());
// 迭代時必須手動同步:
synchronized (syncList) {
for (String s : syncList) { ... }
}
// 2. CopyOnWriteArrayList (讀多寫少、快照迭代)
List cowList = new CopyOnWriteArrayList<>();
// 迭代不需同步,寫入時複製底層陣列
// 3. 並發佇列 (生產者-消費者)
BlockingQueue queue = new LinkedBlockingQueue<>();
需要執行緒安全?
├── 是 → 讀多寫少、需快照迭代? → CopyOnWriteArrayList
│ └── 否 → 生產者消費者模式? → BlockingQueue (LinkedBlockingQueue)
│ └── 否 → 一般同步需求 → Collections.synchronizedList(ArrayList)
└── 否 → 頻繁頭尾增刪、雙端操作? → LinkedList (或 ArrayDeque)
└── 否 → 頻繁隨機存取、遍歷、末端新增? → ArrayList (預設首選)
└── 否 → 中間頻繁增刪? → 考慮 LinkedList 或重新設計資料結構
| 操作 | ArrayList | LinkedList | Vector |
|---|---|---|---|
| 序列新增 add(e) | ~5 ms | ~15 ms | ~45 ms |
| 隨機存取 get(i) | ~0.001 ms | ~120 ms | ~0.001 ms |
| 頭端插入 add(0,e) | ~180 ms | ~0.002 ms | ~350 ms |
| 中間插入 add(n/2,e) | ~90 ms | ~60 ms | ~170 ms |
| 迭代遍歷 | ~3 ms | ~18 ms | ~5 ms |
subList 回傳視圖,結構性修改原列表會導致視圖失效拋 ConcurrentModificationExceptionListIterator 與 Iterator 不可混用於同一列表修改Arrays.asList() 回傳固定大小列表,不可增刪,僅可 setArrayList 會導致資料損毀或無限循環90% 場景首選 ArrayList:快取友善、記憶體效率高、隨機存取 O(1)。LinkedList 專用於頻繁頭尾操作或實作 Deque。Vector 已淘汰,同步需求改用 Collections.synchronizedList、CopyOnWriteArrayList 或並發佇列。理解底層資料結構與效能特性,才能寫出高效、正確的集合操作代碼。