🚀 Java 中的 ArrayList 和 LinkedList 區別詳解(源碼級理解)
在日常 Java 開發中,ArrayList
和 LinkedList
是我們經常用到的兩種 List 實現。雖然它們都實現了 List
接口,但在底層結構、訪問效率、插入/刪除操作、擴容機制等方面差異明顯。
本文從實際源碼出發,結合內存結構與操作成本,詳細分析二者的異同,幫助你更合理地做出選型。
📦 1. ArrayList —— 基于數組的動態列表
? 底層結構
ArrayList
的底層是一個 動態對象數組Object[] elementData
- 內存中是 連續存儲
- 元素個數由
size
字段控制,和數組容量 (elementData.length
) 分開管理
transient Object[] elementData; // 存儲元素的數組
private int size; // 實際元素個數
🚀 訪問性能
- 由于數組結構內存連續,可以通過 起始地址 + 下標 × 元素大小 直接定位
- 訪問效率為 O(1),非常快
🧱 添加元素的邏輯
-
默認容量為 10,首次添加時觸發初始化
-
如果容量不足,會執行 擴容(grow)操作
- 擴容策略:原容量的 1.5 倍
- 核心操作是新建數組 + 拷貝舊數據(
System.arraycopy
)
int newCapacity = oldCapacity + (oldCapacity >> 1);
System.arraycopy(elementData, 0, newArray, 0, size);
🧹 刪除元素的邏輯
- 如果刪除的是中間位置,需要將后面的元素全部 前移一位
- 刪除最后一個元素后,會將該位置置為
null
,防止內存泄露
System.arraycopy(elementData, index + 1, elementData, index, size - index - 1);
elementData[--size] = null;
- ? 不會自動縮容,避免頻繁擴容/縮容帶來的性能波動(可以手動
trimToSize()
)
?? ArrayList 的缺點
- 插入/刪除中間元素時會頻繁移動數組,效率低(O(n))
- 擴容需要申請新數組并復制,性能有開銷
🔗 2. LinkedList —— 基于雙向鏈表的列表結構
? 底層結構
LinkedList
是典型的 雙向鏈表- 每個節點是一個
Node
對象,包含prev
,next
,item
- 元素在內存中 不連續存儲
private static class Node<E> {E item;Node<E> next;Node<E> prev;
}
🧱 插入/刪除性能優勢
- 插入/刪除某個位置時,只需修改相鄰節點的指針,效率高
- ? 不需要擴容,也不會拷貝數組
🐢 訪問性能劣勢
- 鏈表無法通過下標快速定位元素
- 訪問第 n 個元素需要從頭或尾遍歷(O(n))
?? LinkedList 的缺點
- 每個元素多占用 2 個引用(prev 和 next)
- 訪問性能差,尤其在需要頻繁隨機讀取場景下
🧮 ArrayList 與 LinkedList 對比總結
特性 | ArrayList(動態數組) | LinkedList(雙向鏈表) |
---|---|---|
底層結構 | 連續 Object[] 數組 | 不連續的 Node 鏈表 |
訪問速度 | 快,O(1) 隨機訪問 | 慢,需要遍歷,O(n) |
插入刪除 | 慢,需要移動數組元素 | 快,僅需改指針 |
擴容機制 | 支持,擴容為 1.5 倍 | 無需擴容 |
內存使用 | 少(只存數據) | 多(每節點多兩個引用) |
應用場景 | 讀多寫少,隨機訪問場景 | 寫多讀少,插入刪除頻繁場景 |
? 最佳實踐建議
- 讀多寫少、頻繁按下標訪問 ? 用
ArrayList
- 寫多讀少、頻繁插入/刪除中間元素 ? 用
LinkedList
- 如果你在處理的是“隊列”或“棧”場景 ? 也可以考慮
Deque
或ArrayDeque
🔚 總結
理解 ArrayList
和 LinkedList
的底層結構,能幫助你在性能、內存、復雜度方面做出更合理的設計選擇。切記:
不要被接口迷惑,底層結構決定一切。