Java Collections Framework 提供三種主流 List 實作:Vector、ArrayList、LinkedList。它們在隨機存取、插入刪除、記憶體佔用等面向有顯著差異。本文從底層實作角度剖析效能特徵。
兩者內部皆維護一個 Object[] elementData 陣列,並以 size 記錄實際元素數。陣列容量 capacity 可大於 size,預留空間減少擴容頻率。
| 操作 | 時間複雜度 | 說明 |
|---|---|---|
| get(index) | O(1) | 直接陣列索引存取 |
| add(E) | O(1) 平攤 | 尾端新增,偶爾觸發擴容複製 |
| add(index, E) | O(n - index) | 需將 index 之後元素右移一位 |
| remove(index) | O(n - index) | 需將 index 之後元素左移一位 |
public void ensureCapacity(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = Math.max(oldCapacity * 2, minCapacity); // 或 1.5 倍
elementData = new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, size);
}
}
擴容涉及陣列複製,若能預估資料量,建議在建構時指定初始容量:new ArrayList(1000)。
Vector 方法多為 synchronized,執行緒安全但效能較差;ArrayList 非同步,單執行緒環境下更快。Vector 仍有鎖競爭開銷。ArrayList;需執行緒安全時考慮 Collections.synchronizedList 或 CopyOnWriteArrayList。由 Node 節點組成雙向鏈結,每節點持有 prev、next 參考與元素值。維護 first、last 指標與 size。
| 操作 | 時間複雜度 | 說明 |
|---|---|---|
| get(index) | O(min(index, size-index)) | 從頭或尾遍歷較近一端 |
| add(E) | O(1) | 尾端直接插入 |
| add(index, E) | O(min(index, size-index)) | 定位後調整指標,無陣列複製 |
| remove(index) | O(min(index, size-index)) | 定位後調整指標 |
| 場景 | 推薦實作 | 理由 |
|---|---|---|
| 頻繁隨機存取、尾端新增、少量插刪 | ArrayList | O(1) 存取、記憶體緊湊、快取友善 |
| 頻繁頭尾插入刪除、佇列/堆疊 | LinkedList | O(1) 兩端操作、無擴容複製 |
| 中間頻繁插刪、資料量大 | LinkedList | 避免大量陣列位移 |
| 多執行緒共享、需同步 | Collections.synchronizedList(new ArrayList()) | 手動控制同步粒度優於 Vector |
| 讀多寫少、需快照迭代 | CopyOnWriteArrayList | 寫入複製陣列,讀取無鎖 |
實際專案中應依據真實資料分佈與操作模式,使用 JMH(Java Microbenchmark Harness)進行基準測試,而非單純相信理論複雜度。