LinkedList源碼閱讀筆記(1.8)

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的子類,以最大限度地減小「順序訪問」數據存儲實現此接口所需的工做量
    node

  • 實現的接口
    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));
    }

筆記:數據結構

  1. checkElementIndex方法調用isElementIndex方法,是檢驗此參數index是不是現有元素的索引。用於查,改,刪操做的校驗好比:get,set,remove方法調用
  2. 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的方法實現app

// 隊列查詢
    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);
    }

筆記:ide

  1. getFirst,peek,poll方法都是返回第一個元素,區別在於:frist元素爲空時getFirst報異常,peek返回null,poll也返回null;frist元素不爲空時,getFirst,peek僅僅返回first,poll返回以後會將first元素刪除;getFirst,peek至關於查詢,poll至關於取出(出隊)。
  2. checkElementIndex(index),checkPositionIndex(index)的區別,上一標題的筆記
  3. 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();
    }

筆記:以上經過方法的分析能夠得出 隊列,雙向隊列,棧的區別ui

  1. 回顧Queue隊列的入隊(offer)只能從尾部加入,也能出隊(poll)只能從頭部出去:先進先出
  2. Deque雙向隊列,支持在首尾兩端插入(offerFirst,offerLast)和移除(pollFirst,pollLast)元素;
  3. 棧的特色是先進後出,後進先出;
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方法單獨列出來,是由於它是有參構造器須要調用的方法;

  1. 將集合c轉換成集合a
  2. 遍歷a集合,將集合中的元素一一封裝成節點newNode,利用pred變量,一一地接起來
  3. 是不是開始節點?是不是最後節點?這樣細節問題根據具體條件進行操做

迭代器(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();
        }
    }

筆記:

  1. ListItr是雙向的,有next相關方法,也有previous相關方法
  2. index == size時,next爲null,previous()方法返回的就是last節點的元素
  3. remove(),set(E e)方法必需要在next()或previous()以後執行,否則會報IllegalStateException();remove()/set(E e)方法移除/設置的元素就是其前面的next()或previous()返回的這個元素;
  4. add(E e)方法,會將lastReturned = null;
  5. checkForComodification()方法是校驗是否存在併發修改的風險,存在則fast-fail

小言

  源碼版本爲JDK1.8,只是對平常使用的基本操做的源碼進行了分析,對於1.8的新特性並無涉及,等將主要集合類源碼分析完後,之後會專門出一篇分析一下1.8中集合的新特性;   有建議或着問題的,請在文末留言,本人水平有限,有錯誤或理解誤差,還請各位多多指導和見諒,如若轉載,請代表出處;

相關文章
相關標籤/搜索