概述
相較于 ArrayList,LinkedList 在平時使用少一些。
LinkedList 内部是一個雙向連結清單,并且實作了 List 接口和 Deque 接口,是以它也具有 List 的操作以及雙端隊列和棧的性質。雙向連結清單的結構如下:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLi0zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwkzX39GZhh2csATMflHLwEzX4xSZz91ZsADMx8FdsYkRGZkRG9lcvx2bjxSa2EWNhJTW1AlUxEFeVRUUfRHelRHL2EzXlpXazxyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3PnVGcq5SM5ATM4IWYjRTY2cTNzQzYllDZwEDOkNzMmVzMkVmYx8CX1AzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL2M3Lc9CX6MHc0RHaiojIsJye.jpeg)
前文分析了 Queue 和 Deque 接口,正是因為 LinkedList 實作了 Deque 接口。LinkedList 的繼承結構如下:
結點類 Node
檢視 LinkedList 的源碼可發現它内部有個嵌套類 Node,代碼如下:
LinkedList 是雙向連結清單的實作,而該 Node 類則是連結清單的結點。
此外,LinkedList 還有幾個成員變量如下:
構造器
LinkedList 有兩個構造器,如下:
PS: 由于連結清單的容量可以一直增加,是以沒有指定容量的構造器。
其中第一個為無參構造器;第二個為使用指定的集合構造,并調用 addAll(),繼續跟進該方法,代碼如下:
其中 node(index) 方法為擷取指定位置的結點,代碼如下:
該方法通過周遊連結清單擷取指定的元素。
值得注意的是,該方法并非直接從頭到尾周遊整個連結清單,而是先判斷下标的位置,若在前一半則從前往後周遊;否則就從後往前周遊。這樣能減少周遊結點的個數。
PS: 前文「資料結構與算法筆記(一)」對連結清單進行過分析,由于其記憶體空間非連續,是以不支援随機通路(下标通路)。是以,查詢某個結點是通過周遊整個連結清單來實作的。
與此同時,get(index) 方法内部也是這樣實作的:
常用方法
之前分析 Queue 和 Deque 的時候提到:Queue 中的方法在 Deque 中都有對應的。下面簡單分析 LinkedList 中一些常用的方法。
新增結點方法:add(), addLast(), offerLast()
可以看到他們都是調用了同一個方法 linkLast(e) 實作的,如下:
該操作就是将指定的結點添加到連結清單末尾。
删除結點方法:poll(), pollFirst(), removeFirst()
可以看到這三個方法都是調用 unlinkFirst() 方法實作的,其代碼如下:
該方法的操作就是從連結清單頭部移除一個結點。
向單連結清單插入和删除結點的操作示意圖如下(雙連結清單比這裡多了前驅結點):
棧的入棧(push)和出棧(pop)操作:
可以看到這兩個方法直接調用了雙端隊列的實作方法。即,該棧是一個「鍊式棧」。
線程安全性
線程安全的概念不再贅述。分析以下場景:
若有線程 T1 對 LinkedList 進行周遊,同時線程 T2 對其進行結構性修改。
對 LinkedList 的周遊是通過 listIterator(index) 方法實作的,如下:
該類的 next(), add(e) 等方法在執行時會檢測 modCount 與建立時是否一緻(checkForComodification() 方法),進而判斷是否有其他線程對該對象進行了結構修改,若有則抛出 ConcurrentModificationException 異常。
是以,LinkedList 是線程不安全的。
小結
1. LinkedList 内部是「雙向連結清單」,同時實作了 List 接口和 Deque 接口,是以也具備 List、雙端隊列和棧的性質;
2. 線程不安全。
Stay hungry, stay foolish.