jdk1.8.0_45源碼解讀——LinkedList的實現
一、LinkedList概述
LinkedList是List和Deque接口的雙向鏈表的實現。實現了所有可選列表操作,並允許包括null值。
LinkedList既然是通過雙向鏈表去實現的,那么它可以被當作堆棧、隊列或雙端隊列進行操作。並且其順序訪問非常高效,而隨機訪問效率比較低。
注意,此實現不是同步的。 如果多個線程同時訪問一個LinkedList實例,而其中至少一個線程從結構上修改了列表,那么它必須保持外部同步。這通常是通過同步那些用來封裝列表的 對象來實現的。但如果沒有這樣的對象存在,則該列表需要運用{@link Collections#synchronizedList Collections.synchronizedList}來進行“包裝”,該方法最好是在創建列表對象時完成,為了避免對列表進行突發的非同步操作。
List list = Collections.synchronizedList(new LinkedList(...));
類中的iterator()方法和listIterator()方法返回的iterators迭代器是fail-fast的:當某一個線程A通過iterator去遍歷某集合的過程中,若該集合的內容被其他線程所改變了;那么線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生fail-fast事件。
二、LinkedList源碼解析
1.節點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類結構
//通過LinkedList實現的接口可知,其支持隊列操作,雙向列表操作,能被克隆,支持序列化 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { // LinkedList的大小(指其所含的元素個數) transient int size = 0; /** * 指向第一個節點 * 不變的: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; /** * 指向最后一個節點 * 不變的: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node<E> last; ...... }
LinkedList包含了三個重要的對象:first、last 和 size。
(1) first 是雙向鏈表的表頭,它是雙向鏈表節點所對應的類Node的實例
(2) last 是雙向鏈表的最后一個元素,它是雙向鏈表節點所對應的類Node的實例
(3) size 是雙向鏈表中節點的個數。
3. 構造函數
LinkedList提供了兩種種方式的構造器,構造一個空列表、以及構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的。
//構建一個空列表 public LinkedList() { } /** * 構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回的順序排列的 * @param c 包含用於去構造LinkedList的元素的collection * @throws NullPointerException 如果指定的collection為空 */ //構建一個包含指定集合c的列表 public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
4. 添加元素
LinkedList提供了頭插入addFirst(E e)、尾插入addLast(E e)、add(E e)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)、add(int index, E element)這些添加元素的方法。
//頭插入,在列表首部插入節點值e public void addFirst(E e) { linkFirst(e); } //頭插入,即將節點值為e的節點設置為鏈表首節點 private void linkFirst(E e) { final Node<E> f = first; //構建一個prev值為null,節點值為e,next值為f的新節點newNode final Node<E> newNode = new Node<>(null, e, f); //將newNode作為首節點 first = newNode; //如果原首節點為null,即原鏈表為null,則鏈表尾節點也設置為newNode if (f == null) last = newNode; else //否則,原首節點的prev設置為newNode f.prev = newNode; size++; modCount++; } //尾插入,在列表尾部插入節點值e,該方法等價於add() public void addLast(E e) { linkLast(e); } //尾插入,在列表尾部插入節點值e public boolean add(E e) { linkLast(e); return true; } //尾插入,即將節點值為e的節點設置為鏈表的尾節點 void linkLast(E e) { final Node<E> l = last; //構建一個prev值為l,節點值為e,next值為null的新節點newNode final Node<E> newNode = new Node<>(l, e, null); //將newNode作為尾節點 last = newNode; //如果原尾節點為null,即原鏈表為null,則鏈表首節點也設置為newNode if (l == null) first = newNode; else //否則,原尾節點的next設置為newNode l.next = newNode; size++; modCount++; } //中間插入,在非空節點succ之前插入節點值e void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; //構建一個prev值為succ.prev,節點值為e,next值為succ的新節點newNode final Node<E> newNode = new Node<>(pred, e, succ); //設置newNode為succ的前節點 succ.prev = newNode; //如果succ.prev為null,即如果succ為首節點,則將newNode設置為首節點 if (pred == null) first = newNode; else //如果succ不是首節點 pred.next = newNode; size++; modCount++; } /** * 按照指定collection的迭代器所返回的元素順序,將該collection中的所有元素添加到此鏈表的尾部 * 如果指定的集合添加到鏈表的尾部的過程中,集合被修改,則該插入過程的后果是不確定的。 * 一般這種情況發生在指定的集合為該鏈表的一部分,且其非空。 * @throws NullPointerException 指定集合為null */ public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } //從指定的位置開始,將指定collection中的所有元素插入到此鏈表中,新元素的順序為指定collection的迭代器所返回的元素順序 public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); //index >= 0 && index <= size Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; //succ指向當前需要插入節點的位置,pred指向其前一個節點 if (index == size) { //說明在列表尾部插入集合元素 succ = null; pred = last; } else { succ = node(index); //得到索引index所對應的節點 pred = succ.prev; } //指定collection中的所有元素依次插入到此鏈表中指定位置的過程 for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; //將元素值e,前繼節點pred“封裝”為一個新節點newNode Node<E> newNode = new Node<>(pred, e, null); if (pred == null) //如果原鏈表為null,則新插入的節點作為鏈表首節點 first = newNode; else pred.next = newNode; pred = newNode; //pred指針向后移動,指向下一個需插入節點位置的前一個節點 } //集合元素插入完成后,與原鏈表index位置后面的子鏈表鏈接起來 if (succ == null) { //說明之前是在列表尾部插入的集合元素 last = pred; //pred指向的是最后插入的那個節點 } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; } //將指定的元素(E element)插入到列表的指定位置(index) public void add(int index, E element) { checkPositionIndex(index); //index >= 0 && index <= size if (index == size) linkLast(element); //尾插入 else linkBefore(element, node(index)); //中間插入 }
5.刪除元素
LinkedList提供了頭刪除removeFirst()、尾刪除removeLast()、remove(int index)、remove(Object o)、clear()這些刪除元素的方法。
//移除首節點,並返回該節點的元素值 public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } //刪除非空的首節點f private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; //將原首節點的next節點設置為首節點 if (next == null) //如果原鏈表只有一個節點,即原首節點,刪除后,鏈表為null last = null; else next.prev = null; size--; modCount++; return element; } //移除尾節點,並返回該節點的元素值 public E removeLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException(); return unlinkLast(l); } //刪除非空的尾節點l private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; //將原尾節點的prev節點設置為尾節點 if (prev == null) //如果原鏈表只有一個節點,則刪除后,鏈表為null first = null; else prev.next = null; size--; modCount++; return element; } //移除此列表中指定位置上的元素 public E remove(int index) { checkElementIndex(index); //index >= 0 && index < size return unlink(node(index)); } //刪除非空節點x E unlink(Node<E> x) { // assert x != null; 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; } if (next == null) { //如果被刪除節點為尾節點 last = prev; } else { next.prev = prev; x.next = null; } x.item = null; // help GC size--; modCount++; return element; } //移除列表中首次出現的指定元素(如果存在),LinkedList中允許存放重復的元素 public boolean remove(Object o) { //由於LinkedList中允許存放null,因此下面通過兩種情況來分別處理 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 void clear() { // Clearing all of the links between nodes is "unnecessary", but: // - helps a generational GC if the discarded nodes inhabit // more than one generation // - is sure to free memory even if there is a reachable Iterator 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++; }
6.修改元素
LinkedList提供了set(int index, E element)方法來修改指定索引上的值。
//替換指定索引位置節點的元素值,並返回舊值 public E set(int index, E element) { checkElementIndex(index); //index >= 0 && index < size Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
7.查找元素
LinkedList提供了getFirst()、getLast()、contains(Object o)、get(int index)、indexOf(Object o)、lastIndexOf(Object o)這些查找元素的方法。
//返回列表首節點元素值 public E getFirst() { final Node<E> f = first; if (f == null) //如果首節點為null throw new NoSuchElementException(); return f.item; } //返回列表尾節點元素值 public E getLast() { final Node<E> l = last; if (l == null) //如果尾節點為null throw new NoSuchElementException(); return l.item; } //判斷列表中是否包含有元素值o,返回true當列表中至少存在一個元素值e,使得(o==null?e==null:o.equals(e)) public boolean contains(Object o) { return indexOf(o) != -1; } //返回指定索引處的元素值 public E get(int index) { checkElementIndex(index); //index >= 0 && index < size return node(index).item; //node(index)返回指定索引位置index處的節點 } //返回指定索引位置的節點 Node<E> node(int index) { // assert isElementIndex(index); //折半思想,當index < size/2時,從列表首節點向后查找 if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { //當index >= size/2時,從列表尾節點向前查找 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } } //正向查找,返回LinkedList中元素值Object o第一次出現的位置,如果元素不存在,則返回-1 public int indexOf(Object o) { int index = 0; //由於LinkedList中允許存放null,因此下面通過兩種情況來分別處理 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; } //逆向查找,返回LinkedList中元素值Object o最后一次出現的位置,如果元素不存在,則返回-1 public int lastIndexOf(Object o) { int index = size; //由於LinkedList中允許存放null,因此下面通過兩種情況來分別處理 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; }
由LinkedList的類結構可以看出,LinkedList是AbstractSequentialList的子類。AbstractSequentialList 實現了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)這些隨機訪問的函數,那么LinkedList也實現了這些隨機訪問的接口。LinkedList具體是如何實現隨機訪問的?即,具體是如何定義index這個參數的?
在源碼中,Node<E> node(int index)方法是得到索引index所指向的Node節點的。具體實現為:
//返回指定索引位置的節點 Node<E> node(int index) { // assert isElementIndex(index); //折半思想,當index < size/2時,從列表首節點向后查找 if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { //當index >= size/2時,從列表尾節點向前查找 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
該方法返回雙向鏈表中指定位置處的節點,而鏈表中是沒有下標索引的,要指定位置出的元素,就要遍歷該鏈表,從源碼的實現中,我們看到這里有一個加速動作。 源碼中先將index與長度size的一半比較,如果index<size/2,就只從位置0往后遍歷到位置index處,而如果 index>size/2,就只從位置size往前遍歷到位置index處。這樣可以減少一部分不必要的遍歷。
8.其他public方法
clone()、toArray()、toArray(T[] a)
//返回此 LinkedList實例的淺拷貝 public Object clone() { LinkedList<E> clone = superClone(); // Put clone into "virgin" state clone.first = clone.last = null; clone.size = 0; clone.modCount = 0; // Initialize clone with our elements for (Node<E> x = first; x != null; x = x.next) clone.add(x.item); return clone; } //返回一個包含LinkedList中所有元素值的數組 public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; } //如果給定的參數數組長度足夠,則將ArrayList中所有元素按序存放於參數數組中,並返回 //如果給定的參數數組長度小於LinkedList的長度,則返回一個新分配的、長度等於LinkedList長度的、包含LinkedList中所有元素的新數組 @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null; return a; }
支持序列化的寫入函數writeObject(java.io.ObjectOutputStream s)和讀取函數readObject(java.io.ObjectInputStream s)
private static final long serialVersionUID = 876323262645176354L; //序列化:將linkedList的“大小,所有的元素值”都寫入到輸出流中 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out size s.writeInt(size); // Write out all elements in the proper order. for (Node<E> x = first; x != null; x = x.next) s.writeObject(x.item); } //反序列化:先將LinkedList的“大小”讀出,然后將“所有的元素值”讀出 @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in size int size = s.readInt(); // Read in all elements in the proper order. for (int i = 0; i < size; i++) linkLast((E)s.readObject()); //以尾插入的方式 }
9.Queue操作
Queue操作提供了peek()、element()、poll()、remove()、offer(E e)這些方法。
//獲取但不移除此隊列的頭;如果此隊列為空,則返回 null public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //獲取但不移除此隊列的頭;如果此隊列為空,則拋出NoSuchElementException異常 public E element() { return getFirst(); } //獲取並移除此隊列的頭,如果此隊列為空,則返回 null public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //獲取並移除此隊列的頭,如果此隊列為空,則拋出NoSuchElementException異常 public E remove() { return removeFirst(); } //將指定的元素值(E e)插入此列表末尾 public boolean offer(E e) { return add(e); }
10.Deque(雙端隊列)操作
Deque操作提供了offerFirst(E e)、offerLast(E e)、peekFirst()、peekLast()、pollFirst()、pollLast()、push(E e)、pop()、removeFirstOccurrence(Object o)、removeLastOccurrence(Object o)這些方法。
//獲取但不移除此隊列的頭;如果此隊列為空,則返回 null public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //獲取但不移除此隊列的頭;如果此隊列為空,則拋出NoSuchElementException異常 public E element() { return getFirst(); } //獲取並移除此隊列的頭,如果此隊列為空,則返回 null public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //獲取並移除此隊列的頭,如果此隊列為空,則拋出NoSuchElementException異常 public E remove() { return removeFirst(); } //將指定的元素值(E e)插入此列表末尾 public boolean offer(E e) { return add(e); } // Deque operations //將指定的元素插入此雙端隊列的開頭 public boolean offerFirst(E e) { addFirst(e); return true; } //將指定的元素插入此雙端隊列的末尾 public boolean offerLast(E e) { addLast(e); return true; } //獲取,但不移除此雙端隊列的第一個元素;如果此雙端隊列為空,則返回 null public E peekFirst() { final Node<E> f = first; return (f == null) ? null : f.item; } //獲取,但不移除此雙端隊列的最后一個元素;如果此雙端隊列為空,則返回 null public E peekLast() { final Node<E> l = last; return (l == null) ? null : l.item; } //獲取並移除此雙端隊列的第一個元素;如果此雙端隊列為空,則返回 null public E pollFirst() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //獲取並移除此雙端隊列的最后一個元素;如果此雙端隊列為空,則返回 null public E pollLast() { final Node<E> l = last; return (l == null) ? null : unlinkLast(l); } //將一個元素推入此雙端隊列所表示的堆棧(換句話說,此雙端隊列的頭部) public void push(E e) { addFirst(e); } //從此雙端隊列所表示的堆棧中彈出一個元素(換句話說,移除並返回此雙端隊列的頭部) public E pop() { return removeFirst(); } //從此雙端隊列移除第一次出現的指定元素,如果列表中不包含次元素,則沒有任何改變 public boolean removeFirstOccurrence(Object o) { return remove(o); } //從此雙端隊列移除最后一次出現的指定元素,如果列表中不包含次元素,則沒有任何改變 public boolean removeLastOccurrence(Object o) { //由於LinkedList中允許存放null,因此下面通過兩種情況來分別處理 if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { //逆向向前 if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = last; x != null; x = x.prev) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
11. Fail-Fast機制
LinkedList也采用了快速失敗的機制,通過記錄modCount參數來實現。在面對並發的修改時,迭代器很快就會完全失敗,而不是冒着在將來某個不確定時間發生任意不確定行為的風險。
三、常用的LinkedList的遍歷方式
LinkedList不提倡運用隨機訪問的方式進行元素遍歷。
1)通過迭代器Iterator遍歷:
Iterator iter = list.iterator();
while (iter.hasNext()) { System.out.println(iter.next()); }
2)通過迭代器ListIterator遍歷:
ListIterator<String> lIter = list.listIterator();
//順向遍歷 while(lIter.hasNext()){ System.out.println(lIter.next()); } //逆向遍歷 while(lIter.hasPrevious()){ System.out.println(lIter.previous()); }
3)foreach循環遍歷
for(String str:list) { System.out.println(str); }
四、LinkedList的運用
LinkedList實現堆棧:Java實現棧和隊列
【感謝】

