天天看點

【JDK】JDK源碼分析-LinkedList

概述

相較于 ArrayList,LinkedList 在平時使用少一些。

LinkedList 内部是一個雙向連結清單,并且實作了 List 接口和 Deque 接口,是以它也具有 List 的操作以及雙端隊列和棧的性質。雙向連結清單的結構如下:

【JDK】JDK源碼分析-LinkedList

前文分析了 Queue 和 Deque 接口,正是因為 LinkedList 實作了 Deque 接口。LinkedList 的繼承結構如下:

【JDK】JDK源碼分析-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() 方法實作的,其代碼如下:

該方法的操作就是從連結清單頭部移除一個結點。

向單連結清單插入和删除結點的操作示意圖如下(雙連結清單比這裡多了前驅結點):

【JDK】JDK源碼分析-LinkedList

棧的入棧(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.

【JDK】JDK源碼分析-LinkedList

繼續閱讀