LinkedList原始碼閱讀筆記(1.8)
阿新 • • 發佈:2019-01-11
目錄
LinkedList類的註解閱讀
/** * Doubly-linked list implementation of the {@code List} and {@code Deque} * interfaces. Implements all optional list operations, and permits all * elements (including {@code null}). * * <p>All of the operations perform as could be expected for a doubly-linked * list. Operations that index into the list will traverse the list from * the beginning or the end, whichever is closer to the specified index. 雙鏈表實現了List和Deque的介面,實現所有可選列表操作,並允許所有操作元素(包括null) 對於雙向連結串列,所有操作都可以預期。 索引到列表中的操作將從開頭或結尾遍歷列表,以較接近指定索引為準。
- Deque介面就是雙向佇列,是Queue(佇列)的一個子介面,雙向佇列是指該佇列兩端的元素既能入隊(offer)也能出隊(poll);
如果將Deque限制為只能從一端入隊和出隊,則可實現棧的資料結構。對於棧而言,有入棧(push)和出棧(pop),遵循先進後出原則。
* * Note that this implementation is not synchronized.</strong> * If multiple threads access a linked list concurrently, and at least * one of the threads modifies the list structurally, it <i>must</i> be * synchronized externally. (A structural modification is any operation * that adds or deletes one or more elements; merely setting the value of * an element is not a structural modification.) This is typically * accomplished by synchronizing on some object that naturally * encapsulates the list. * * * If no such object exists, the list should be "wrapped" using the * {@link Collections#synchronizedList Collections.synchronizedList} * method. This is best done at creation time, to prevent accidental * unsynchronized access to the list:<pre> * List list = Collections.synchronizedList(new LinkedList(...));</pre> 請注意,此實現不同步。 如果多個執行緒同時訪問連結串列,並且至少有一個執行緒在結構上修改了列表,則必須在外部進行同步。 (結構修改是新增或刪除一個或多個元素的任何操作;僅設定元素的值不是結構修改。)這通常通過同步自然封裝列表的某個物件來完成。 如果不存在此類物件,則應使用Collections.synchronizedList}方法“包裝”該列表。 這最好在建立時完成,以防止對列表的意外不同步訪問: List list = Collections.synchronizedList(new LinkedList(...))
- LinkedList執行緒不同步,可以使用synchronizedList包裝此列表,使其執行緒安全
* The iterators returned by this class's {@code iterator} and * {@code listIterator} methods are <i>fail-fast</i>: if the list is * structurally modified at any time after the iterator is created, in * any way except through the Iterator's own {@code remove} or * {@code add} methods, the iterator will throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than * risking arbitrary, non-deterministic behavior at an undetermined * time in the future. * 這個類的iterator和listIterator方法返回的迭代器是 fail-fast 機制:如果在建立迭代器之後的任何時候對列表進行了結構修改,除了通過Iterator自己的 remove或 add方法之外,迭代器將丟擲 ConcurrentModificationException。 因此,在併發修改的情況下,迭代器快速而乾淨地失敗,而不是在未來的未確定時間冒任意,非確定性行為的風險。
- LinkedList的迭代器也是採用快速失敗的策略,在閱讀ArrayList原始碼的時候分析過(參考閱讀原始碼JDK1.8(集合篇)- ArrayList)
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
注意,迭代器的fail-fast行為是不能保證的.一般來說,保證非同步的同步操作是不太可能的.在最優基礎上,Fail-fast迭代器會丟擲ConcurrentModificationException.因此,寫一個為了自身正確性而依賴於這個異常的程式是不對的.迭代器的fail-fast行為應該只是用來檢測bug而已.
- 我們要主動封裝list以便進行同步操作,程式要要避免此異常而不是使用此異常
LinkedList類的定義
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
繼承的類
AbstractSequentialList:抽象類,AbstractList的子類,以最大限度地減少“順序訪問”資料儲存實現此介面所需的工作量實現的介面
List:不多說了
Deque:(標記介面)Deque介面就是雙向佇列,是Queue(佇列)的一個子介面
Cloneable:(標記介面)代表 Object.clone() 方法可以合法地對該類例項進行按欄位複製。(沒有實現 Cloneable 介面的例項上呼叫 Object 的 clone 方法,則會導致丟擲 CloneNotSupportedException 異常)
java.io.Serializable(標記介面)
屬性的定義
protected transient int modCount = 0;
- 這是父類AbstractList的一個屬性 :用於記錄列表結構被修改的次數。每次列表結構被修改都會modCount++
為什麼要記錄此資料呢?
線上程不安全的集合中,正如上面所說:迭代器採用了fail-fast機制。而fail-fast機制觸發原理就是比對expectedModCount 和 modCount 是否相等,不相等就報ConcurrentModificationException異常
此處不理解沒關係,後面會講迭代器方法的原始碼時,就會明白了
transient int size = 0;
- transient修飾不可被序列化
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*
* 指向第一個節點的指標
*/
transient Node<E> first;
- 儲存第一個節點的Node例項,記住一個定律:當first為null時,last必為null,此時list為empty或null;當first.prev 為null時,說明至少有一個元素存在,first.item必不為空;當只存在一個元素時,它即是first又是last
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*
* 指向最後一個節點的指標。
*
*/
transient Node<E> last;
- 儲存最後一個節點的Node例項,記住一個定律:當first為null時,last必為null,此時list為empty或null;當last.next 為null時,說明至少有一個元素存在,last.item必不為空;當只存在一個元素時,它即是first又是last
內部類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;
}
}
- 分成三部分 prev:儲存前一位的Node,first的prev為null; item:儲存元素;next:儲存後一位的Node,last的next為null;
LinkedList構造器
/**
* Constructs an empty list.
*
* 構造一個空列表。
*
*/
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
*
* 按照集合的迭代器返回的順序構造一個包含指定集合元素的列表。
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
- 相當於一個空LinkedList呼叫addAll()方法
核心方法
/**
* Returns the (non-null) Node at the specified element index.
*
* 返回指定元素索引處的(非null)節點。
*
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
// 一個個賦值,目的是將第index個賦值
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
- LinkedList並不支援隨機訪問,所以根據index來返回對應元素效率很低。 注意:size >> 1 相當於除以2,一分為二決定從頭部開始遍歷還是從尾部開始遍歷,提高效率
/**
* Links e as first element.
*
* 將e作為第一個元素
*
*/
private void linkFirst(E e) {
final Node<E> f = first;
// 新建一個元素為e的Node例項,next指標指向first
final Node<E> newNode = new Node<>(null, e, f);
// 將元素為e的Node例項作為first
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
/**
* Inserts the specified element at the beginning of this list.
*
* 在list的開頭新增指定元素,就是呼叫上方法
*
* @param e the element to add
*/
public void addFirst(E e) {
// 直接呼叫如上方法
linkFirst(e);
}
- 簡單分步理解:第一步:建立元素為e的Node例項:newNode;第二步:newNode成為first,並且它的next結點指向舊first;第三步:若舊first不存在,newNode也當作last,若舊first存在,則舊first的prev結點要指向newNode;這屬於結構修改,所以modCount++
/**
* Links e as last element.
*/
private void linkLast(E e) {
final Node<E> l = last;
// 新建一個元素為e的Node例項,pre指標指向first
final Node<E> newNode = new Node<>(l, e, null);
// 將元素為e的Node例項作為last
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
* @param e the element to add
*/
public void addLast(E e) {
linkLast(e);
}
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*/
public boolean add(E e) {
linkLast(e);
return true;
}
- 簡單理解:(同上,試著自己總結一下),linkLast(E e)被addLast,add方法所使用。
/**
* Inserts element e before non-null Node succ.
*
* 在非null節點succ之前插入元素e。
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
此方法會被add(int index, E element)使用
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index)); // 這裡的node(index)就使用了一開始說的根據index返回對應的元素的方法
}
- 簡單分步理解:第一步:建立元素為e的Node例項:newNode,且將prev結點指向succ前一位node;第二步:succ的prev結點指向newNode;第三步:若succ是first,newNode就是first,若succ不是first,則succ的前結點的next要指向newNode;這屬於結構修改,所以modCount++
/**
* Unlinks non-null first node 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;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
- f.item = null;f.next = null;置為空求助於GC回收。first賦值為其next
/**
* Unlinks non-null last node l.
*
* 刪除非空的最後節點f
*/
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;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
- 與unlinkFirst異曲同工
/**
* Unlinks non-null node x.
*
* 刪除指定的非空的節點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;
size--;
modCount++;
return element;
}
- 邏輯不復雜,其中所有置空操作都是為了讓GC回收,以上的方法(除了node(int index))都要modCount++,因為改變了結構
校驗方法
/**
* Tells if the argument is the index of an existing element.
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
/**
* Tells if the argument is the index of a valid position for an
* iterator or an add operation.
*/
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
/**
* Constructs an IndexOutOfBoundsException detail message.
* Of the many possible refactorings of the error handling code,
* this "outlining" performs best with both server and client VMs.
*/
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));
}
筆記:
- checkElementIndex方法呼叫isElementIndex方法,是檢驗此引數index是否是現有元素的索引。用於查,改,刪操作的校驗比如:get,set,remove方法呼叫
- checkPositionIndex方法呼叫isPositionIndex方法,是校驗此引數index是否是迭代器或新增操作的有效位置的索引。用於add,addAll和迭代器相關方法呼叫
普通方法
大部分查改的方法都是內部呼叫的以上介紹的核心方法
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);
}
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));
}
Queue operations. 以下為介面Queue的方法實現
// 佇列查詢
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E element() {
return getFirst();
}
// 出隊
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E remove() {
return removeFirst();
}
// 入隊
public boolean offer(E e) {
return add(e);
}
筆記:
- getFirst,peek,poll方法都是返回第一個元素,區別在於:frist元素為空時getFirst報異常,peek返回null,poll也返回null;frist元素不為空時,getFirst,peek僅僅返回first,poll返回之後會將first元素刪除;getFirst,peek相當於查詢,poll相當於取出(出隊)。
- checkElementIndex(index),checkPositionIndex(index)的區別,上一標題的筆記
- set(int index, E element)有返回值,返回的是舊元素值(oldVal),這點注意。
Deque operations 以下為雙向佇列Deque的方法實現
這裡列出重要的四種6種方法:
當作雙向佇列時的入隊(頭或尾),出隊(頭或尾)四個方法;
當作棧使用時的入棧(push)和出棧(pop)兩個方法;
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
/**
* Pushes an element onto the stack represented by this list. In other
* words, inserts the element at the front of this list.
*
* 將元素推送到此列表所表示的堆疊上。 換句話說,將元素插入此列表的前面。
*
* <p>This method is equivalent to {@link #addFirst}.
*/
public void push(E e) {
addFirst(e);
}
/**
* Pops an element from the stack represented by this list. In other
* words, removes and returns the first element of this list.
*
* 彈出此列表所代表的堆疊中的元素。 換句話說,刪除並返回此列表的第一個元素。
*
* <p>This method is equivalent to {@link #removeFirst()}.
*/
public E pop() {
return removeFirst();
}
筆記:以上通過方法的分析可以得出 佇列,雙向佇列,棧的區別
- 回顧Queue佇列的入隊(offer)只能從尾部加入,也能出隊(poll)只能從頭部出去:先進先出
- Deque雙向佇列,支援在首尾兩端插入(offerFirst,offerLast)和移除(pollFirst,pollLast)元素;
- 棧的特點是先進後出,後進先出;
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* 從指定位置開始,將指定集合中的所有元素插入此列表。
* 將當前位置的元素(如果有)和任何後續元素向右移動(增加其索引)。
* 新元素將按照指定集合的迭代器返回的順序出現在列表中。
*
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
// 將c轉化成陣列
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
筆記:addAll方法單獨列出來,是因為它是有參構造器需要呼叫的方法;
- 將集合c轉換成集合a
- 遍歷a集合,將集合中的元素一一封裝成節點newNode,利用pred變數,一一地接起來
- 是否是開始節點?是否是最後節點?這樣細節問題根據具體條件進行操作
迭代器(iterator&ListIterator)實現
Iterator
/**
* @since 1.6
*/
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
/**
* Adapter to provide descending iterators via ListItr.previous
*
* 通過ListItr.previous提供降序迭代器
*/
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
- Iterator返回的是DescendingIterator本質上就是ListItr,區別在於DescendingIterator的hasNext方法相當於ListItr的hasPrevious方法,next方法是ListItr的previous方法
ListIterator
/**
* Returns a list-iterator of the elements in this list (in proper
* sequence), starting at the specified position in the list.
* Obeys the general contract of {@code List.listIterator(int)}.<p>
*
* 從列表中的指定位置開始,返回此列表中元素的列表迭代器(按正確順序)。
*
* The list-iterator is <i>fail-fast</i>: if the list is structurally
* modified at any time after the Iterator is created, in any way except
* through the list-iterator's own {@code remove} or {@code add}
* methods, the list-iterator will throw a
* {@code ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
*/
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index); // 校驗是否是有效位置
return new ListItr(index);
}
private class ListItr implements ListIterator<E> {
// 儲存上一個返回的節點
private Node<E> lastReturned;
// 儲存即將返回的節點
private Node<E> next;
// 儲存即將返回的元素的index
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {
return nextIndex > 0;
}
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
筆記:
- ListItr是雙向的,有next相關方法,也有previous相關方法
- index == size時,next為null,previous()方法返回的就是last節點的元素
- remove(),set(E e)方法必須要在next()或previous()之後執行,不然會報IllegalStateException();remove()/set(E e)方法移除/設定的元素就是其前面的next()或previous()返回的這個元素;
- add(E e)方法,會將lastReturned = null;
- checkForComodification()方法是校驗是否存在併發修改的風險,存在則fast-fail
小言
原始碼版本為JDK1.8,只是對日常使用的基本操作的原始碼進行了分析,對於1.8的新特性並沒有涉及,等將主要集合類原始碼分析完後,以後會專門出一篇分析一下1.8中集合的新特性;
有建議或著問題的,請在文末留言,本人水平有限,有錯誤或理解偏差,還請各位多多指導和見諒,如若轉載,請表明出處;