LinkedList的是基於鏈表實現的java集合類,經過index插入到指定位置的時候使用LinkedList效率要比ArrayList高,如下源碼分析是基於JDK1.8.java
LinkedList類的繼承結構如以下所示:
從以上繼承結構圖中能夠看到,最頂層接口爲Iterable接口,實現此接口的類支持經過迭代器遍歷集合中的元素。其餘相關接口如Collection、List、Queue、Deque分別爲列表,隊列功能的相關接口,即此LinkedList支持列表、隊列的相關操做。Serializable接口爲序列化標誌接口,即全部須要序列化的類都須要實現此接口。node
首先咱們來看下LinkedList中保存數據的內部類定義源碼以下:安全
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
經過以上定義能夠看出來每一個節點中除了保存元素的值以外還保存了先後節點的引用。即便用了鏈表的數據接口保存數據元素。數據結構以下圖所示:
在LinkedList類中,只是定義了兩個Node類型的屬性,定義以下:數據結構
transient Node<E> first; transient Node<E> last;
這兩個屬性分別表示頭節點和尾節點,始終指向鏈表的第一個節點和最後一個節點。併發
當不指定index往LinkedList中添加元素的時候默認是在鏈表的尾部添加數據,源代碼以下源碼分析
void linkLast(E e) { //保存鏈表插入以前的最後一個節點 final Node<E> l = last; //將新節點的preNode指向鏈表最後一個節點 final Node<E> newNode = new Node<>(l, e, null); last指向插入後的尾節點 last = newNode; if (l == null) //當第一次插入數據的時候,頭節點和尾節點指向同一個節點 first = newNode; else //插入以前的尾節點的nextNode指向新插入的節點 l.next = newNode; size++; modCount++; }
插入節點的詳細操做如上面代碼註釋。在次操做中其實last即尾節點是共享資源,當多個線程同時執行此方法的時候,其實會出現線程安全問題。具體的線程安全問題後面分析。this
當指定index插入數據的時候,源代碼以下所示:spa
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }
在指定index插入元素的時候會先查詢出index位置的元素,而後調用linkBefore將此元素插入到查詢到的元素的前一個位置。其中linkBefore源碼以下所示:線程
void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; //新建節點,並將preNode指向idnex-1節點 final Node<E> newNode = new Node<>(pred, e, succ); //插入前的index節點的prev指向新節點 succ.prev = newNode; if (pred == null) first = newNode; else //index-1節點的nextNode指向新節點 pred.next = newNode; size++; modCount++; }
在指定節點插入的方式如上註釋所示。此操做中共享資源是插入以前index節點。一樣會出現併發安全問題,下面對此問題進行分析。code
在指定index插入或者addLast的時候都是在鏈表的尾部插入數據,當併發插入的時候若是出現如下執行順序的時候,會出現插入的數據被覆蓋的問題。
當多個線程同時獲取到相同的尾節點的時候,而後多個線程同時在此尾節點後面插入數據的時候會出現數據覆蓋的問題。