1. 程式人生 > 實用技巧 >JDK原始碼筆記-LinkedList

JDK原始碼筆記-LinkedList

1、概述

LinkedList ,基於節點實現的雙向連結串列的 List ,每個節點都指向前一個和後一個節點從而形成連結串列。

相比 ArrayList 來說,我們日常開發使用 LinkedList 相對比較少

2、類圖

LinkedList 實現的介面、繼承的抽象類,如下圖所示:

如下 3 個介面是 ArrayList 一致的:

如下 1 個介面是少於 ArrayList 的:

如下 1 個介面是多於 ArrayList 的:

  • java.util.Deque介面,提供雙端佇列的功能,LinkedList 支援快速的在頭尾新增元素和讀取元素,所以很容易實現該特性。

繼承了java.util.AbstractSequentialList抽象類,它是 AbstractList 的子類,實現了只能連續訪問“資料儲存”(例如說連結串列)的#get(int index)#add(int index, E element)等等隨機操作的方法

3、屬性

  LinkedList 一共有3個屬性。如下圖所示:

  • 通過 Node 節點指向前後節點,從而形成雙向連結串列。
  • firstlast屬性:連結串列的頭尾指標。
    • 在初始時候,firstlast指向null,因為此時暫時沒有 Node 節點。
    • 在新增完首個節點後,建立對應的 Node 節點node1,前後指向null。此時,firstlast指向該 Node 節點。
    • 繼續新增一個節點後,建立對應的 Node 節點node2,其prev = node1next = null,而node1prev = nullnext = node2。此時,first保持不變,指向node1last發生改變,指向node2
  • size屬性:連結串列的節點數量。通過它進行計數,避免每次需要 List 大小時,需要從頭到尾的遍歷。
/**
 * 連結串列大小
 */
transient int size = 0;

/**
 * 頭節點
 *
 * Pointer to first node.
 */
transient Node<E> first;

/**
 * 尾節點
 *
 * Pointer to last node.
 */
transient Node<E> last;

/**
 * 節點
 *
 * @param <E> 元素泛型
 */
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;
    }

}

4、提供的方法

(1)構造方法

  LinkedList 一共有兩個構造方法,我們分別來看看

public LinkedList() {
}

public LinkedList(Collection<? extends E> c) {
    this();
    // 新增 c 到連結串列中
    addAll(c);
}

  相比 ArrayList 來說,因為沒有容量一說,所以不需要提供#ArrayList(int initialCapacity)這樣的構造方法。

(2)新增單個元素

  #add(E e)方法,順序新增單個元素到連結串列。程式碼如下:

public boolean add(E e) {
        //新增到末尾
        linkLast(e);
        return true;
    }

void linkLast(E e) {
        //記錄原last節點
        final Node<E> l = last;
        //建立新節點
        // l 是newNode前一個節點
        // e 是節點元素
        //null 是newNode的下一個節點
        final Node<E> newNode = new Node<>(l, e, null);
        //last指向新節點
        last = newNode;
        //如果 l 為空,則first節點也為空,那麼first指向新節點
        if (l == null)
            first = newNode;
        //如果 l 不為空,則first節點也不為空,那麼last指向新節點
        else
            l.next = newNode;
        //連結串列長度+1
        size++;
        //運算元+1
        modCount++;
    }

  #add(int index, E element)方法,插入單個元素到指定位置

public void add(int index, E element) {
        //校驗是否超出範圍
        checkPositionIndex(index);
        //如果index = size,就是插入到尾部
        if (index == size)
            //實際上就是add
            linkLast(element);
        else
            linkBefore(element, node(index));
    } 

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指向新節點
            first = newNode;
        else
            //否則前節點的後節點指向新節點
            pred.next = newNode;
        size++;
        modCount++;
    }   

  #node(int index)方法,獲得第index個 Node 節點

Node<E> node(int index) {
        // assert isElementIndex(index);
        //size >> 1 位運算 結果大概為size的一半
        //如果index在前半部分,正序遍歷,獲取第index的節點
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            //在後半部分,就倒序遍歷,獲取第index的節點
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

  #linkBefore(E e, Node<E> succ)方法,新增元素esucc節點的前面,與linkLast邏輯基本相同

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++;
    }

  因為 LinkedList 實現了 Deque 介面,所以它實現了#addFirst(E e)#addLast(E e)方法,分別新增元素到連結串列的頭尾

public void addFirst(E e) {
    linkFirst(e);
}
public boolean offerFirst(E e) {
    addFirst(e); // 呼叫上面的方法
    return true;
}

public void addLast(E e) {
    linkLast(e);
}
public boolean offerLast(E e) {
    addLast(e); // 呼叫上面的方法
    return true;
}

  linkLast方法就不多說了,下面是#linkFirst(E e)方法,新增元素到隊頭

private void linkFirst(E e) {
        //記錄first節點
        final Node<E> f = first;
        //建立新節點
        final Node<E> newNode = new Node<>(null, e, f);
        //first指向新節點
        first = newNode;
        //如果first節點為空,則last節點也為空,那麼last也指向新節點
        if (f == null)
            last = newNode;
        else
            //原first節點的前節點指向新節點
            f.prev = newNode;
        size++;
        modCount++;
    }

  整體來說,新增單個元素,分為三個方法:

  • 新增到隊頭
  • 新增到隊尾
  • 新增到中間

(3)新增多個元素

  #addAll(Collection<? extends E> c)方法,批量新增多個元素

public boolean addAll(int index, Collection<? extends E> c) {
        
        checkPositionIndex(index);
        //要新增的陣列
        Object[] a = c.toArray();
        //要新增陣列的長度
        int numNew = a.length;
        if (numNew == 0)
            return false;
        //新建兩個節點,之後操作
        Node<E> pred, succ;
        //如果index==size,那麼就相當於在隊尾新增,則succ=null,pred=las
        //如果index不等於size,那麼就相當於在中間新增,則succ為index位置的節點,pred為index的前節點
        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);
            //判斷pred是否為null,如果為null,那麼pred也為null,那麼first指向新節點
            //如果不為null,index位置的前一個節點指向新節點
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            //pred指向新節點
            pred = newNode;
        }
        //如果succ為null,那麼last也為null,則last指向pred
        //不為null,那麼pred的next指向後節點,後節點的prev指向前節點
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }

(4)移除單個元素

  #remove(int index)方法,移除指定位置的元素,並返回該位置的原元素

public E remove(int index) {
        checkElementIndex(index);
        //呼叫node方法獲得第index位置的節點,然後刪除
        return unlink(node(index));
    }

E unlink(Node<E> x) {
        // assert x != null;
        //拿到x的元素
        final E element = x.item;
        //拿到x的後節點
        final Node<E> next = x.next;
        //拿到x的前節點
        final Node<E> prev = x.prev;
        
        //如果x的前節點為null,那麼將first指向x的後節點,可以理解為移除隊頭元素
        //不為null,那麼將x的前節點的next指向x的後節點,並將x的prev置null
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        //和前節點部分同裡,只不過後節點為null時,為移除隊尾元素
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

  #remove(Object o)方法,移除首個為o的元素,並返回是否移除到

public boolean remove(Object o) {
        //元素為null時,遍歷連結串列,找到null的元素,刪除
        //不為null時,遍歷連結串列,找的和o元素相等的節點,刪除
        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;
    }

  #removeFirstOccurrence(Object o)#removeLastOccurrence(Object o)方法,分別實現移除連結串列首個節點和最後節點

public boolean removeFirstOccurrence(Object o) { // 移除首個
    return remove(o);
}

public boolean removeLastOccurrence(Object o) {
    if (o == null) { // o 為 null 的情況
        // 倒序遍歷,找到 null 的元素後,進行移除
        for (Node<E> x = last; x != null; x = x.prev) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        // 倒序遍歷,找到等於 o 的元素後,進行移除
        for (Node<E> x = last; x != null; x = x.prev) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

  #remove()方法,移除連結串列首個節點

public E remove() {
        return removeFirst();
    }

public E removeFirst() {
        final Node<E> f = first;
        //連結串列為空時,丟擲異常
        if (f == null)
            throw new NoSuchElementException();
        //移除連結串列首個節點
        return unlinkFirst(f);
    }

private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //拿到首個節點元素
        final E element = f.item;
        //拿到首個節點指向的後節點
        final Node<E> next = f.next;
        //元素置null,後節點指向置null
        f.item = null;
        f.next = null; // help GC
        //first指向首個節點指向的後節點
        first = next;
        //如果後節點為null,那麼整個連結串列時null的,那麼last也為null
        //不為空的話,後節點的前節點指向置空
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

  #removeLast()方法,移除連結串列最後一個節點,和#removeFirst()方法思路是差不多的

public E removeLast() {
    final Node<E> l = last;
    // 如果連結串列為空,則丟擲 NoSuchElementException 移除
    if (l == null)
        throw new NoSuchElementException();
    // 移除連結串列的最後一個元素
    return unlinkLast(l);
}

private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    final E element = l.item;
    // 獲得 f 的上一個節點
    final Node<E> prev = l.prev;
    // 設定 l 的 item 為 null ,幫助 GC
    l.item = null;
    // 設定 l 的 prev 為 null ,幫助 GC
    l.prev = null; // help GC
    // 修改 last 指向 prev
    last = prev;
    // 修改 prev 節點的 next 指向 null
    if (prev == null) // 如果連結串列只有一個元素,說明被移除後,佇列就是空的,則 first 設定為 null
        first = null;
    else
        prev.next = null;
    // 連結串列大小減一
    size--;
    // 增加陣列修改次數
    modCount++;
    return element;
}

  #poll()#pop()方法,移除連結串列的頭或尾,差異點在於連結串列為空時候,不會丟擲 NoSuchElementException 異常

public E poll() { // 移除頭
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);
}

public E pop() {
    return removeFirst(); 
}

// LinkedList.java 實現 Deque 介面

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);
}

(5)移除多個元素

  #removeAll(Collection<?> c)方法,批量移除指定的多個元素

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    // 獲得迭代器
    Iterator<?> it = iterator();
    // 通過迭代器遍歷
    while (it.hasNext()) {
        // 如果 c 中存在該元素,則進行移除
        if (c.contains(it.next())) {
            it.remove();
            modified = true; // 標記修改
        }
    }
    return modified;
}

  #retainAll(Collection<?> c)方法,求 LinkedList 和指定多個元素的交集。簡單來說,恰好和#removeAll(Collection<?> c)相反,移除不在c中的元素

// AbstractCollection.java

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    // 獲得迭代器
    Iterator<E> it = iterator();
    // 通過迭代器遍歷
    while (it.hasNext()) {
        // <X> 如果 c 中不存在該元素,則進行移除
        if (!c.contains(it.next())) {
            it.remove();
            modified = true;
        }
    }
    return modified;
}

(6)查詢單個元素

  #indexOf(Object o)方法,查詢首個為指定元素的位置

public int indexOf(Object o) {
        int index = 0;
        if (o == null) {//如果o為null
            //迴圈遍歷
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;//找到返回
                index++;
            }
        } else {//如果o不為null
            //迴圈遍歷
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;//找到返回
                index++;
            }
        }
        return -1;
    }

  而#contains(Object o)方法,就是基於該方法實現

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

  有時我們需要查詢最後一個為指定元素的位置,所以會使用到#lastIndexOf(Object o)方法

public int lastIndexOf(Object o) {
    int index = size;
    if (o == null) { // 如果 o 為 null 的情況
        // 倒序遍歷,如果 item 為 null 的節點,進行返回
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (x.item == null)
                return index; // 找到
        }
    } else { // 如果 o 非 null 的情況
        // 倒序遍歷,如果 item 為 o 的節點,進行返回
        for (Node<E> x = last; x != null; x = x.prev) {
            index--;
            if (o.equals(x.item))
                return index; // 找到
        }
    }
    // 未找到
    return -1;
}

(7)獲得指定位置的元素

  #get(int index)方法,獲得指定位置的元素

public E get(int index) {
    checkElementIndex(index);
    // 基於 node(int index) 方法實現
    return node(index).item;
}

  因為 LinkedList 實現了 Deque 介面,所以它實現了#peekFirst()#peekLast()方法,分別獲得元素到連結串列的頭尾

public E peekFirst() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

public E peekLast() {
    final Node<E> l = last;
    return (l == null) ? null : l.item;
}

  因為 LinkedList 實現了 Queue 介面,所以它實現了#peek()#element()方法,分別獲得元素到連結串列的頭

public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

public E element() { // 如果連結串列為空識,丟擲 NoSuchElementException 異常
    return getFirst();
}
public E getFirst() {
    final Node<E> f = first;
    if (f == null) // 如果連結串列為空識,丟擲 NoSuchElementException 異常
        throw new NoSuchElementException();
    return f.item;
}

(8)獲取指定位置的元素

  #set(int index, E element)方法,設定指定位置的元素

public E set(int index, E element) {
        checkElementIndex(index);
        //獲取index的節點
        Node<E> x = node(index);
        E oldVal = x.item;
        //設定index節點的元素
        x.item = element;
        return oldVal;
    }

(9)轉換為陣列

  #toArray()方法,將 ArrayList 轉換成[]陣列

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;
    }

  實際場景下,我們可能想要指定T泛型的陣列,那麼我們就需要使用到#toArray(T[] a)方法

public <T> T[] toArray(T[] a) {
        //如果傳入陣列長度比size小,直接複製一個數組返回
        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;
        //如果傳入的陣列長度大於size,那麼將size位置置null
        if (a.length > size)
            a[size] = null;

        return a;
    }

(10)求雜湊值

  #hashCode()方法,求 LinkedList 的雜湊值

public int hashCode() {
    int hashCode = 1;
    // 遍歷,求雜湊
    for (E e : this)
        hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    return hashCode;
}

(11)判斷相等

  #equals(Object o)方法,判斷是否相等

public boolean equals(Object o) {
    // 如果 o 就是自己,直接返回 true
    if (o == this)
        return true;
    // 如果不為 List 型別,直接返回 false
    if (!(o instanceof List))
        return false;

    // 建立迭代器,順序遍歷比對
    ListIterator<E> e1 = listIterator();
    ListIterator<?> e2 = ((List<?>) o).listIterator();
    while (e1.hasNext() && e2.hasNext()) {
        E o1 = e1.next();
        Object o2 = e2.next();
        if (!(o1==null ? o2==null : o1.equals(o2))) // 如果不相等,返回 false
            return false;
    }
    // 如果有迭代器沒有遍歷完,說明兩者長度不等,所以就不相等;否則,就相等了
    return !(e1.hasNext() || e2.hasNext());
}

(12)清空連結串列

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; ) {
            //拿到x的後節點
            Node<E> next = x.next;
            //將前後節點指向和元素全部置空
            x.item = null;
            x.next = null;
            x.prev = null;
            //x指向x的後節點
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

(13)克隆

  #clone()方法,克隆 LinkedList 物件

public Object clone() {
        //呼叫父類clone方法
        LinkedList<E> clone = superClone();

        // Put clone into "virgin" state
        //將clone重置為初始狀態,此處注意,first、last等都是重新初始化的,不與前LinkedList共享
        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;
    }

(14)建立子陣列

  #subList(int fromIndex, int toIndex)方法,建立 ArrayList 的子陣列

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size());
    // 根據判斷 RandomAccess 介面,判斷是否支援隨機訪問
    return (this instanceof RandomAccess ?
            new RandomAccessSubList<>(this, fromIndex, toIndex) :
            new SubList<>(this, fromIndex, toIndex));
}
  • 該方法,是通過父類 AbstractList 來實現的。
  • 根據判斷 RandomAccess 介面,判斷是否支援隨機訪問,從而建立 RandomAccessSubList 或 SubList 物件

(15)建立Iterator迭代器

  #iterator()方法,建立迭代器

public Iterator<E> iterator() {
    return listIterator();
}

// AbstractList.java
public ListIterator<E> listIterator() {
    return listIterator(0);
}

// AbstractSequentialList.java
public abstract ListIterator<E> listIterator(int index);
  • 該方法,是通過父類 AbstractSequentialList 來實現的。
  • 整個呼叫過程是,iterator() => listIterator() => listIterator(int index)的順序,就是我們在程式碼裡貼進去的順序。最終呢,是呼叫 LinkedList 對#listIterator(int index)的實現

(16)建立LIstIterator迭代器

  #listIterator(int index)方法,建立 ListIterator 迭代器

public ListIterator<E> listIterator(int index) {
    checkPositionIndex(index);
    return new ListItr(index);
}

  因為 ListItr 的實現程式碼比較簡單,我們就不逐個來看了,直接貼加了註釋的程式碼

private class ListItr implements ListIterator<E> {

    /**
     * 最後返回的節點
     */
    private Node<E> lastReturned;
    /**
     * 下一個節點
     */
    private Node<E> next;
    /**
     * 下一個訪問元素的位置,從下標 0 開始。
     *
     * 主要用於 {@link #nextIndex()} 中,判斷是否遍歷結束
     */
    private int nextIndex;
    /**
     * 建立迭代器時,陣列修改次數。
     *
     * 在迭代過程中,如果陣列發生了變化,會丟擲 ConcurrentModificationException 異常。
     */
    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();
        // 如果已經遍歷到結尾,丟擲 NoSuchElementException 異常
        if (!hasNext())
            throw new NoSuchElementException();

        // lastReturned 指向,記錄最後訪問節點
        lastReturned = next;
        // next 指向,下一個節點
        next = next.next;
        // 下一個節點的位置 + 1
        nextIndex++;
        // 返回 lastReturned
        return lastReturned.item;
    }

    public boolean hasPrevious() {
        return nextIndex > 0;
    }

    public E previous() {
        // 校驗是否陣列發生了變化
        checkForComodification();
        // 如果已經遍歷到結尾,丟擲 NoSuchElementException 異常
        if (!hasPrevious())
            throw new NoSuchElementException();

        // 修改 lastReturned 和 next 的指向。此時,lastReturned 和 next 是相等的。
        lastReturned = next = (next == null) ? last : next.prev;
        // 下一個節點的位置 - 1
        nextIndex--;
        // 返回 lastReturned
        return lastReturned.item;
    }

    public int nextIndex() {
        return nextIndex;
    }

    public int previousIndex() {
        return nextIndex - 1;
    }

    public void remove() {
        // 校驗是否陣列發生了變化
        checkForComodification();
        // 如果 lastReturned 為空,丟擲 IllegalStateException 異常,因為無法移除了。
        if (lastReturned == null)
            throw new IllegalStateException();

        // 獲得 lastReturned 的下一個
        Node<E> lastNext = lastReturned.next;
        // 移除 lastReturned 節點
        unlink(lastReturned);
        // 此處,會分成兩種情況
        if (next == lastReturned) // 說明發生過呼叫 `#previous()` 方法的情況,next 指向下一個節點,而 nextIndex 是無需更改的
            next = lastNext;
        else
            nextIndex--; // nextIndex 減一。

        // 設定 lastReturned 為空
        lastReturned = null;
        // 增加陣列修改次數
        expectedModCount++;
    }

    public void set(E e) {
        // 如果 lastReturned 為空,丟擲 IllegalStateException 異常,因為無法修改了。
        if (lastReturned == null)
            throw new IllegalStateException();
        // 校驗是否陣列發生了變化
        checkForComodification();
        // 修改 lastReturned 的 item 為 e
        lastReturned.item = e;
    }

    public void add(E e) {
        // 校驗是否陣列發生了變化
        checkForComodification();
        // 設定 lastReturned 為空
        lastReturned = null;
        // 此處,會分成兩種情況
        if (next == null) // 如果 next 已經遍歷到尾,則 e 作為新的尾節點,進行插入。算是效能優化
            linkLast(e);
        else // 插入到 next 的前面
            linkBefore(e, next);
        // nextIndex 加一。
        nextIndex++;
        // 增加陣列修改次數
        expectedModCount++;
    }

    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        // 遍歷剩餘連結串列
        while (modCount == expectedModCount && nextIndex < size) {
            // 執行 action 邏輯
            action.accept(next.item);
            // lastReturned 指向 next
            lastReturned = next;
            //  next 指向下一個節點
            next = next.next;
            // nextIndex 加一。
            nextIndex++;
        }
        // 校驗是否陣列發生了變化
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

}