先簡單描述一下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增刪快,查詢慢,從這個方法能夠知道,其實更準確的說,是在末端增長元素時才快,指定位置插入時,仍是須要先進行遍歷查詢,速度同樣慢.指針