《JDK源碼閱讀三》--LinkedList雙向鏈表詳解

先簡單描述一下LinkedList的數據結構和特性:

LinkedList和ArrayList都實現了List接口,但LinkedList底層是雙向鏈表,因此不存在索引,node

查詢時:LinkedList須要從鏈表頭部或者鏈表尾部遍歷查詢全部節點,因此查詢較慢,數據結構

刪除時:LinkedList只須要改變指針的指向,並把要刪除的節點置爲null,便可,不須要改變元素位置,因此刪除較快.(圖中全部的地址,表示節點的地址)app

1.先看看LinkedList的屬性都有哪些:

   transient int size = 0; // 儲存的節點個數 
    transient Node<E> first; // 指定第一個節點的指針 
    transient Node<E> last; // 指定最後一個節點的指針

  LinkedList中的節點類Node:

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; } }

2.LinkedList的構造方法:

  /** * 建立一個空list */
    public LinkedList() { } /** * 按照集合的迭代器返回的順序構造一個包含指定集合元素的列表。*/
    public LinkedList(Collection<? extends E> c) { this(); // 先調用無參構造方法,建立一個空列表 addAll(c); // 而後調用addAll()方法,將集合中的元素按照順序,逐一插入鏈表中 }

3.一些關鍵方法

Node<E> node(int index) 獲取索引處的Node節點

  /** * Returns the (non-null) Node at the specified element index. */ Node<E> node(int index) { // assert isElementIndex(index);

        if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }

這個方法解釋了爲何LinkedList查詢慢,由於鏈表沒有全部索引,只有頭節點和尾結點,查詢元素時,判斷索引在整個鏈表是偏左仍是偏右,進行遍歷查找,而後將其返回this

private void linkFirst(E e)  將該元素做爲鏈表的第一個元素

/** * Links e as first element. */
    private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }

void linkLast(E e)  將該元素做爲鏈表的最後一個元素

void linkLast(E e) { final Node<E> l = last; // 由於l看不清,說明的時候用大寫L替換 final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }

這兩個方法很類似,以linkLast(E e)方法爲例講解,新建節點L指向last,表示爲鏈表原末端節點,新建節點newNode,放到鏈表末端,其prev指針指向L,next指針設爲null,表示爲新的末端節點spa

判斷原末端節點L是否爲null,若爲空說明,未初始化鏈表頭節點,此時將newNode做爲頭結點,若不爲空,將原末端節點的next指針,指向新節點newNode.3d

E unlink(Node<E> x)  取消該非空節點連接

 1     /**
 2  * Unlinks non-null node x.  3      */
 4     E unlink(Node<E> x) {  5         // assert x != null;
 6         final E element = x.item;  7         final Node<E> next = x.next;  8         final Node<E> prev = x.prev;  9       
10         if (prev == null) { 11             first = next; 12         } else { 13             prev.next = next; 14             x.prev = null; 15  } 16 
17         if (next == null) { 18             last = prev; 19         } else { 20             next.prev = prev; 21             x.next = null; 22  } 23 
24         x.item = null; 25         size--; 26         modCount++; 27         return element; 28     }
首先獲取當前節點的上一個節點prev和下一個節點next,
  若是上一個節點prev爲空,說明當前節點爲頭節點,須要將當前節點的下一個節點next設置爲頭節點.
  不然,將當前節點的上一個節點prev的next指針,指向當前節點的下一個節點;
  若是下一個節點next爲空,說明當前節點爲末端節點,須要將當前節點的上一個節點prev設置爲末端節點.
  不然,將當前節點的下一個節點next的prev指針,指向當前節點的上一個節點;
而後,節點元素設置爲null,等待垃圾回收器回收.

public boolean addAll(int index, Collection<? extends E> c)  將集合中全部的元素,插入到鏈表的指定位置,並按照迭代器返回的順序顯示

 1   /**
 2  * Inserts all of the elements in the specified collection into this  3  * list, starting at the specified position. Shifts the element  4  * currently at that position (if any) and any subsequent elements to  5  * the right (increases their indices). The new elements will appear  6  * in the list in the order that they are returned by the  7  * specified collection's iterator.  8  *  9  * @param index index at which to insert the first element 10  * from the specified collection 11  * @param c collection containing elements to be added to this list 12  * @return {@code true} if this list changed as a result of the call 13  * @throws IndexOutOfBoundsException {@inheritDoc} 14  * @throws NullPointerException if the specified collection is null 15      */
16     public boolean addAll(int index, Collection<? extends E> c) { 17  checkPositionIndex(index); // 索引越界驗證 18 
19         Object[] a = c.toArray(); 20         int numNew = a.length; 21         if (numNew == 0) 22             return false; 23 
24         Node<E> pred, succ; 25         if (index == size) { // 在末端節點後插入元素 26             succ = null; 27             pred = last; 28         } else { 29             succ = node(index); 30             pred = succ.prev; // 獲取指定位置節點的上一節點 31  } 32 
33         for (Object o : a) { // 遍歷集合中全部的元素, 34             @SuppressWarnings("unchecked") E e = (E) o; 35             Node<E> newNode = new Node<>(pred, e, null); 36             if (pred == null) // 說明鏈表爲空鏈表,設置頭部節點 37                 first = newNode; 38             else
39                 pred.next = newNode; // 當前索引處節點的上一節點的next指針,指向新節點 40             pred = newNode; 41  } 42 
43         if (succ == null) { // 若是爲在末端節點後插入,那麼設置末端節點 44             last = pred; 45         } else { 46             pred.next = succ; // 將新插入完成的最後一個節點和索引處的原節點進行關聯 47             succ.prev = pred; 48  } 49 
50         size += numNew; 51         modCount++; 52         return true; 53     }
 
 

原來一直說LinkedList增刪快,查詢慢,從這個方法能夠知道,其實更準確的說,是在末端增長元素時才快,指定位置插入時,仍是須要先進行遍歷查詢,速度同樣慢.指針

相關文章
相關標籤/搜索