1. 程式人生 > >Java之LinkedList原始碼解讀(JDK 1.8)

Java之LinkedList原始碼解讀(JDK 1.8)

java.util.LinkedList

  •     雙向連結串列實現的List。
  •     基於JDK 1.8。
  •     沒有使用標準的註釋,並適當調整了程式碼的縮排以方便介紹。
  •     裡面很多方法的實現是一樣的,不過可以讓外界感覺其提供了更多的行為。
  •     需要花比ArrayList更多一點的時間理解
package com.anxpp.thinkinjava.chapter11.sourse;
import java.util.AbstractSequentialList;
import java.util.Collection;
import java.util.ConcurrentModificationException
; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; /** * LinkedList底層使用雙向連結串列,實現了List和deque。實現所有的可選List操作,並可以只有所有元素(包括空值)
* 其大小理論上僅受記憶體大小的限制 * * 所有的操作都可以作為一個雙聯列表來執行(及對雙向連結串列操作)。 * 把對連結串列的操作封裝起來,並對外提供看起來是對普通列表操作的方法。 * 遍歷從起點、終點、或指定位置開始 * 內部方法,註釋會描述為節點的操作(如刪除第一個節點),公開的方法會描述為元素的操作(如刪除第一個元素) * * LinkedList不是執行緒安全的,如果在多執行緒中使用(修改),需要在外部作同步處理。 * * 需要弄清元素(節點)的索引和位置的區別,不然有幾個地方不好理解,具體在碰到的地方會解釋。 * * 迭代器可以快速報錯 */ public
class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { //容量 transient int size = 0; //首節點 transient Node<E> first; //尾節點 transient Node<E> last; //預設建構函式 public LinkedList() { } //通過一個集合初始化LinkedList,元素順序有這個集合的迭代器返回順序決定 public LinkedList(Collection<? extends E> c) { this(); addAll(c); } //使用對應引數作為第一個節點,內部使用 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; //如果之前首節點為空(size==0),那麼尾節點就是首節點 else f.prev = newNode; //如果之前首節點不為空,之前的首節點的前一個節點為當前首節點 size++; //長度+1 modCount++; //修改次數+1 } //使用對應引數作為尾節點 void linkLast(E e) { final Node<E> l = last; //得到尾節點 final Node<E> newNode = new Node<>(l, e, null);//使用引數建立一個節點 last = newNode; //設定尾節點 if (l == null) first = newNode; //如果之前尾節點為空(size==0),首節點即尾節點 else l.next = newNode; //如果之前尾節點不為空,之前的尾節點的後一個就是當前的尾節點 size++; modCount++; } //在指定節點前插入節點,節點succ不能為空 void linkBefore(E e, Node<E> succ) { final Node<E> pred = succ.prev;//獲取前一個節點 final Node<E> newNode = new Node<>(pred, e, succ);//使用引數建立新的節點,向前指向前一個節點,向後指向當前節點 succ.prev = newNode;//當前節點指向新的節點 if (pred == null) first = newNode;//如果前一個節點為null,新的節點就是首節點 else pred.next = newNode;//如果存在前節點,那麼前節點的向後指向新節點 size++; modCount++; } //刪除首節點並返回刪除前首節點的值,內部使用 private E unlinkFirst(Node<E> f) { final E element = f.item;//獲取首節點的值 final Node<E> next = f.next;//得到下一個節點 f.item = null; f.next = null; //便於垃圾回收期清理 first = next; //首節點的下一個節點成為新的首節點 if (next == null) last = null; //如果不存在下一個節點,則首尾都為null(空表) else next.prev = null;//如果存在下一個節點,那它向前指向null size--; modCount++; return element; } //刪除尾節點並返回刪除前尾節點的值,內部使用 private E unlinkLast(Node<E> l) { final E element = l.item;//獲取值 final Node<E> prev = l.prev;//獲取尾節點前一個節點 l.item = null; l.prev = null; //便於垃圾回收期清理 last = prev; //前一個節點成為新的尾節點 if (prev == null) first = null; //如果前一個節點不存在,則首尾都為null(空表) else prev.next = null;//如果前一個節點存在,先後指向null size--; modCount++; return element; } //刪除指定節點並返回被刪除的元素值 E unlink(Node<E> x) { //獲取當前值和前後節點 final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; //如果前一個節點為空(如當前節點為首節點),後一個節點成為新的首節點 } else { prev.next = next;//如果前一個節點不為空,那麼他先後指向當前的下一個節點 x.prev = null; //方便gc回收 } if (next == null) { last = prev; //如果後一個節點為空(如當前節點為尾節點),當前節點前一個成為新的尾節點 } else { next.prev = prev;//如果後一個節點不為空,後一個節點向前指向當前的前一個節點 x.next = null; //方便gc回收 } x.item = null; //方便gc回收 size--; modCount++; return element; } //獲取第一個元素 public E getFirst() { final Node<E> f = first;//得到首節點 if (f == null) //如果為空,丟擲異常 throw new NoSuchElementException(); return f.item; } //獲取最後一個元素 public E getLast() { final Node<E> l = last;//得到尾節點 if (l == null) //如果為空,丟擲異常 throw new NoSuchElementException(); return l.item; } //刪除第一個元素並返回刪除的元素 public E removeFirst() { final Node<E> f = first;//得到第一個節點 if (f == null) //如果為空,丟擲異常 throw new NoSuchElementException(); return unlinkFirst(f); } //刪除最後一個元素並返回刪除的值 public E removeLast() { final Node<E> l = last;//得到最後一個節點 if (l == null) //如果為空,丟擲異常 throw new NoSuchElementException(); return unlinkLast(l); } //新增元素作為第一個元素 public void addFirst(E e) { linkFirst(e); } //店家元素作為最後一個元素 public void addLast(E e) { linkLast(e); } //檢查是否包含某個元素,返回bool public boolean contains(Object o) { return indexOf(o) != -1;//返回指定元素的索引位置,不存在就返回-1,然後比較返回bool值 } //返回列表長度 public int size() { return size; } //新增一個元素,預設新增到末尾作為最後一個元素 public boolean add(E e) { linkLast(e); return true; } //刪除指定元素,預設從first節點開始,刪除第一次出現的那個元素 public boolean remove(Object o) { //會根據是否為null分開處理。若值不是null,會用到物件的equals()方法 if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; } //新增指定集合的元素到列表,預設從最後開始新增 public boolean addAll(Collection<? extends E> c) { return addAll(size, c);//size表示最後一個位置,可以理解為元素的位置分別為1~size } //從指定位置(而不是下標!下標即索引從0開始,位置可以看做從1開始,其實也是0)後面新增指定集合的元素到列表中,只要有至少一次新增就會返回true //index換成position應該會更好理解,所以也就是從索引為index(position)的元素的前面索引為index-1的後面新增! //當然位置可以為0啊,為0的時候就是從位置0(雖然它不存在)後面開始新增嘛,所以理所當前就是新增到第一個位置(位置1的前面)的前面了啊! //比如列表:0 1 2 3,如果此處index=4(實際索引為3),就是在元素3後面新增;如果index=3(實際索引為2),就在元素2後面新增。 //原諒我的表達水平,我已經盡力解釋了... public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); //檢查索引是否正確(0<=index<=size) Object[] a = c.toArray(); //得到元素陣列 int numNew = a.length; //得到元素個數 if (numNew == 0) //若沒有元素要新增,直接返回false return false; Node<E> pred, succ; if (index == size) { //如果是在末尾開始新增,當前節點後一個節點初始化為null,前一個節點為尾節點 succ = null; //這裡可以看做node(index),不過index=size了(index最大隻能是size-1),所以這裡的succ只能=null,也方便後面判斷 pred = last; //這裡看做noede(index-1),當然實現是不能這麼寫的,看做這樣只是為了好理解,所以就是在node(index-1的後面開始新增元素) } else { //如果不是從末尾開始新增,當前位置的節點為指定位置的節點,前一個節點為要新增的節點的前一個節點 succ = node(index); //新增好元素後(整個新加的)的後一個節點 pred = succ.prev; //這裡依然是node(index-1) } //遍歷陣列並新增到列表中 for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null);//建立一個節點,向前指向上面得到的前節點 if (pred == null) first = newNode; //若果前節點為null,則新加的節點為首節點 else pred.next = newNode;//如果存在前節點,前節點會向後指向新加的節點 pred = newNode; //新加的節點成為前一個節點 } if (succ == null) { //pred.next = null //加上這句也可以更好的理解 last = pred; //如果是從最後開始新增的,則最後新增的節點成為尾節點 } else { pred.next = succ; //如果不是從最後開始新增的,則最後新增的節點向後指向之前得到的後續第一個節點 succ.prev = pred; //當前,後續的第一個節點也應改為向前指向最後一個新增的節點 } size += numNew; modCount++; return true; } //清空表 public void clear() { //方便gc回收垃圾 for (Node<E> x = first; x != null; ) { Node<E> next = x.next; x.item = null; x.next = null; x.prev = null; x = next; } first = last = null; size = 0; modCount++; } //獲取指定索引的節點的值 public E get(int index) { checkElementIndex(index); return node(index).item; } //修改指定索引的值並返回之前的值 public E set(int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; } //指定位置後面(即索引為這個值的元素的前面)新增元素 public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); //如果指定位置為最後,則新增到連結串列最後 else //如果指定位置不是最後,則新增到指定位置前 linkBefore(element, node(index)); } //刪除指定位置的元素, public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } //檢查索引是否超出範圍,因為元素索引是0~size-1的,所以index必須滿足0<=index<size private boolean isElementIndex(int index) { return index >= 0 && index < size; } //檢查位置是否超出範圍,index必須在index~size之間(含),如果超出,返回false private boolean isPositionIndex(int index) { return index >= 0 && index <= size; } //異常詳情 private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } //檢查元素索引是否超出範圍,若已超出,就丟擲異常 private void checkElementIndex(int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //檢查位置是否超出範圍,若已超出,就丟擲異常 private void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //獲取指定位置的節點 Node<E> node(int index) { //如果位置索引小於列表長度的一半(或一半減一),從前面開始遍歷;否則,從後面開始遍歷 if (index < (size >> 1)) { Node<E> x = first;//index==0時不會迴圈,直接返回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; } } //獲取指定元素從first開始的索引位置,不存在就返回-1 //不能按條件雙向找了,所以通常根據索引獲得元素的速度比通過元素獲得索引的速度快 public int indexOf(Object o) { int index = 0; if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; } //獲取指定元素從first開始最後出現的索引,不存在就返回-1 //但實際查詢是從last開始的 public int lastIndexOf(Object o) { int index = size; if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { index--; if (x.item == null) return index; } } else { for (Node<E> x = last; x != null; x = x.prev) { index--; if (o.equals(x.item)) return index; } } return -1; } //提供普通佇列和雙向佇列的功能,當然,也可以實現棧,FIFO,FILO //出隊(從前端),獲得第一個元素,不存在會返回null,不會刪除元素(節點) public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //出隊(從前端),不刪除元素,若為null會丟擲異常而不是返回null public E element() { return getFirst(); } //出隊(從前端),如果不存在會返回null,存在的話會返回值並移除這個元素(節點) public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //出隊(從前端),如果不存在會丟擲異常而不是返回null,存在的話會返回值並移除這個元素(節點) public E remove() { return removeFirst(); } //入隊(從後端),始終返回true public boolean off