Java集合(四) LinkedList詳解

weixin_33762321發表於2017-12-12

  在上篇文章Java集合(三) ArrayList詳解的學習和原始碼分析中,我們知道ArrayList是以陣列實現,它的優勢是查詢效能高,劣勢是按順序增刪效能差。如果在不確定元素數量的情況時,不建議使用ArrayList。其實當這種情況時,我們就可以使用LinkedList了。

大O記號

  一直以來我們用快慢來描述儲存演算法,這種直觀的描述是不好的。有一種方法可以量化一種操作的耗時情況。我們可以借用數學分析中的大O記號來描述這種關係。
  解決一個規模為n的問題所花費的步驟如果是n的常數倍,我們就記這個方法的複雜度為O(n)。例如,給一個陣列,求這個陣列中的最大值,需要遍歷這個陣列,如果陣列長度為n,那麼遍歷陣列的步驟就是n。所以,這是一種O(n)的操作。同樣,解決一個規模為n的問題所花費的步驟如果是n^2的常數倍,我們就記這個方法的複雜度為O(n^2)。比如氣泡排序。而解決問題的步驟如果與問題規模無關,這個時間複雜度就是O(1)的。比如,陣列的隨機存取。而陣列的順序儲存就是O(n)的。

連結串列

  在學習LinkedList之前,我們先對資料結構中連結串列做一個簡單的回顧。連結串列是一種物理儲存單元上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。連結串列由一系列結點(連結串列中每一個元素成為結點)組成,結點可以在執行時動態生成。
可以這樣理解:
  有一條街,小明住在街中一角,他有小紅的地址,然後小紅也是住在這條街,他有小花的地址,同樣小花也有別人的地址。某天我想找小紅玩,但是我不知道她住在哪裡,我可以問小明,就知道小紅住在哪裡了。那麼小明小紅小花這些人之間的關係就組成一個連結串列。

  • 單連結串列:就是小明只是右手握著小紅的地址,他只有小紅一個人的地址
  • 雙連結串列:就是小紅左手握著小明的地址,右手握著小花的地址,她有兩個人的地址
  • 迴圈連結串列:就是小明握著小紅的地址,小紅握著小花的地址,而小花又握著小明的地址,這樣就形成了一個迴圈
  • 有序連結串列:以某個標準,給連結串列的元素排序,比如比較內容大小、比較雜湊值等 如果還不好理解,我們可以先看一個類的定義:
public class LinkNode {
    public int data;
    public LinkNode next;

    public LinkNode(int data) {
        this.data = data;
        this.next = null;
    }
}
複製程式碼

我們定義了一個LinkNode類,這個類有兩個成員變數,一個是整形data,還有一個是指向LinkNode型別的引用。通過這個引用,就可以把多個LinkNode串起來,比如,下面的程式碼:

        LinkNode head = new LinkNode(1);
        head.next = new LinkNode(2);
複製程式碼

這樣就在記憶體裡建立了兩個LinkNode物件,它們之間的關係就像這樣:

head所引用的那個LinkNode,其data為1,其next指向下一個LinkNode,其data為2,next域為空。

連結串列增加一項

  如果我想在head之後,增加一個連結串列項,讓這個連結串列變成下圖的樣子,該怎麼做?

  思路就是先把新的結點的next設為head的next,然後再使得head.next指向新的結點即可。

        LinkNode newNode = new LinkNode(3);
        newNode.next = head.next;
        head.next = newNode;
複製程式碼

  反過來,想要把head後面的那個結點刪掉,也很簡單,只需要讓head的next再往後指一項,把原來head後面的那一項跳過去就可以了:

        head.next = head.next.next;
複製程式碼

  可以看到,在連結串列中,指定的結點後面新增一個項是常數時間的,也就是O(1),刪除一個項也是一樣的。但與陣列不同的,如果我要完成“查詢連結串列中的第n項是什麼”這個操作,就會變成O(n)的,因為我們每次查詢都必須從頭開始,依次向後查詢。

 public static int queryData(LinkNode head, int index) {
        LinkNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }

        return cur.data;
    }
複製程式碼

  再看一個問題,假如我知道了一個LinkNode,不妨記為temp,位於一個連結串列中,如果想知道這個結點的上一個結點是什麼,應該怎麼辦?對於我們前邊定義的連結串列結構,只能從頭開始找,去判斷是否有某個結點,它的next與temp相等。如果相等就說明,這個結點就是temp的前序結點。程式碼如下:

public static LinkNode findPrevious(LinkNode head, LinkNode temp) {
        LinkNode cur = head;
        while (cur != null) {
            if (cur.next == temp)
                return cur;

            cur = cur.next;
        }

        return null;
    }
複製程式碼

雙向連結串列

  實際上,在工程實踐中,使用連結串列的時候,經常會有“查詢某個結點的前一結點”這樣的需求,為了加速這一過程,我們其實可以修改一下LinkNode的定義,結它加上一個指向前序結點的成員變數:

public class DoubleLinkNode {
    public int data;
    public LinkNode next;
    public LinkNode prev;

    public DoubleLinkNode(int data) {
        this.data = data;
        this.next = null;
        this.prev = null;
    }
}
複製程式碼

這樣的話,連結串列就成了這個樣子了:

  這樣一來,查詢某個結點的上一個結點,只要返回它的prev就可以了。
  好了。連結串列的學習就到這裡了。

LinkedList

概述

  LinkedList以雙向連結串列實現。連結串列無容量限制,但雙向連結串列本身使用了更多空間,也需要額外的連結串列指標操作。除了實現List介面外,LinkedList還為在列表的開頭及結尾get、remove和insert元素提供了統一的命名方法。這些操作可以將連結列表當作棧,佇列和雙端佇列來使用。
  按索引訪問元素:get(i)/set(i,e)要非常噁心的遍歷連結串列將指標移動到位(如果i > 陣列大小的一半,會從末尾開始移動)。插入、刪除元素時修改前後節點的指標即可,但還是要遍歷部分連結串列的指標才能移動到下標所指的位置,只有在連結串列兩頭的操作:add(),addFirst(),removeLast()或用iterator()上的remove()能省掉指標的移動。
  LinkedList同樣是非執行緒安全的,只在單執行緒下適合使用。如果多個執行緒同時訪問一個連結列表,而其中至少一個執行緒從結構上修改了該列表,則它必須保持外部同步。(結構修改指新增或刪除一個或多個元素的任何操作;僅設定元素的值不是結構修改。)這一般通過對自然封裝該列表的物件進行同步操作來完成。如果不存在這樣的物件,則應該使用 Collections.synchronizedList 方法來“包裝”該列表。
  LinkedList的iterator和listIterator方法返回的迭代器是快速失敗的(fail-fast機制):在迭代器建立之後,如果從結構上對列表進行修改,除非通過迭代器自身的remove或add方法,其他任何時間任何方式的修改,迭代器都將丟擲 ConcurrentModificationException。
  LinkedList實現了Serializable介面,因此它支援序列化,能夠通過序列化傳輸,實現了Cloneable介面,能被克隆。

LinkedList原始碼解析

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable,
        java.io.Serializable {
    //LinkedList中連結串列元素個數
    transient int size = 0;
    //連結串列頭結點
    transient Node<E> first;
    //連結串列尾結點
    transient Node<E> last;

    //預設構造方法,生成一個空的連結串列
    public LinkedList() {
    }

    //根據c裡面的元素生成一個LinkedList
    public LinkedList(Collection<? extends E> c) {
        //呼叫空的構造方法
        this();
        //將c裡面的元素新增到空連結串列尾部
        addAll(c);
    }

    //首部增加結點,結點的值為e
    private void linkFirst(E e) {

        final Node<E> f = first;//f指向頭結點
        //生成一個新結點,結點的值為e,其前向指標為null,後向指標為f
        final Node<E> newNode = new Node<>(null, e, f);
        //first指向新生成的結點,f儲存著舊的頭結點資訊
        first = newNode;
        if (f == null)
            //如果f為null,則表示整個連結串列目前是空的,則尾結點也指向新結點
            last = newNode;
        else
            //f(老的頭結點)的前向指向最新的結點資訊
            f.prev = newNode;
        size++;//元素個數+1
        modCount++;//修改次數+1
    }

    //尾部增加結點,結點的值為e
    void linkLast(E e) {
        final Node<E> l = last; //l指向尾結點
        //生成一個新結點,結點的值為e,其前向指標為l,後向指標為null
        final Node<E> newNode = new Node<>(l, e, null);
        //last指向新生成的結點,l儲存著舊的尾結點資訊
        last = newNode;
        if (l == null)
            //如果l為null,則表示整個連結串列目前是空的,則頭結點也指向新結點
            first = newNode;
        else
            //l(舊的尾結點)的後向指標指向最新的結點資訊
            l.next = newNode;
        size++;//元素個數+1
        modCount++;//修改次數+1
    }

    //非空結點succ之前插入新結點,新結點的值為e
    void linkBefore(E e, Node<E> succ) {
        // assert succ != null; //外界呼叫需保證succ不為null,否則程式會丟擲空指標異常
        final Node<E> pred = succ.prev;//pred指向succ的前向結點
        //生成一個新結點,結點的值為e,其前向指標指向pred,後向指標指向succ
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;//succ的前向指標指向newNode
        if (pred == null)
            //如果pred為null,則表示succ為頭結點,此時頭結點指向最新生成的結點newNode
            first = newNode;
        else
            //pred的後向指標指向新生成的結點,此時已經完成了結點的插入操作
            pred.next = newNode;
        size++;//元素個數+1
        modCount++;//修改次數+1
    }

    //刪除頭結點,並返回該結點的值
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;//需確保f為頭結點,且連結串列不為null
        final E element = f.item;//獲得結點的值
        final Node<E> next = f.next;//next指向f的後向結點
        f.item = null;//釋放資料結點
        f.next = null; // help GC   //釋放f的後向指標
        first = next;   //first指向f的後向結點
        if (next == null)
            //如果next為null,則表示f為last結點,此時連結串列即為空連結串列
            last = null;
        else
            //修改next的前向指標,因為first結點的前向指標為null
            next.prev = null;
        size--; //元素個數-1
        modCount++; //修改次數+1
        return element;
    }

    //刪除尾結點,並返回尾結點的內容
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;   //需確保l為尾結點,且連結串列不為null
        final E element = l.item;   //獲得結點的值
        final Node<E> prev = l.prev;    //prev執行1的前向結點
        l.item = null;  //釋放l結點的值
        l.prev = null; // help GC   //釋放l結點的前向指標
        last = prev;    //last結點指向l的前向結點
        if (prev == null)
            //如果prev為null,則表示l為first結點,此時連結串列即為空連結串列
            first = null;
        else
            //修改prev的後向指標,因為last結點的後向指標為null
            prev.next = null;
        size--;//元素個數-1
        modCount++;//修改次數+1
        return element;
    }

    //刪除結點x
    E unlink(Node<E> x) {
        // assert x != null;    //需確保x不為null,否則後續操作會丟擲空指標異常
        final E element = x.item;   //儲存x結點的值
        final Node<E> next = x.next;//next指向x的後向結點
        final Node<E> prev = x.prev;//prev指向x的前向結點

        if (prev == null) {
            //如果prev為空,則x結點為first結點,此時first結點指向next結點(x的後向結點)
            first = next;
        } else {
            prev.next = next;//x的前向結點的後向指標指向x的後向結點
            x.prev = null;  //釋放x的前向指標
        }

        if (next == null) {
            //如果next結點為空,則x結點為尾部結點,此時last結點指向prev結點(x的前向結點)
            last = prev;
        } else {
            next.prev = prev;//x的後向結點的前向指標指向x的前向結點
            x.next = null;  //釋放x的後向指標
        }

        x.item = null;  //釋放x的值節點,此時x節點可以完全被GC回收
        size--; //元素個數-1
        modCount++; //修改次數+1
        return element;
    }

    //獲得頭結點的值
    public E getFirst() {
        final Node<E> f = first;//f指向first結點
        if (f == null)//如果連結串列為空
            throw new NoSuchElementException();
        return f.item;//返回first結點的值
    }

    //獲得尾結點的值
    public E getLast() {
        final Node<E> l = last; //l指向last結點
        if (l == null)//如果連結串列為空
            throw new NoSuchElementException();
        return l.item;//返回last結點的值
    }

    //移除頭結點
    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);//摘除尾結點
    }

    //新增到頭結點,結點的值為e
    public void addFirst(E e) {
        linkFirst(e);//新增到頭部
    }

    //新增到尾結點
    public void addLast(E e) {
        linkLast(e);//新增到尾部
    }

    //判斷元素(值為o)是否o在連結串列中
    public boolean contains(Object o) {
        return indexOf(o) != -1;//定位元素
    }

    //返回元素個數
    public int size() {
        return size;
    }

    //新增元素,元素值為e
    public boolean add(E e) {
        linkLast(e);//新增到連結串列尾部
        return true;
    }

    //移除值為o的元素,o可以為null,找到一個刪除即返回
    public boolean remove(Object o) {
        if (o == null) {//元素為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;
    }

    //將c中的元素都新增到當前連結串列中
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);//新增到連結串列尾部
    }

    //在序號為index處,新增c中所有的元素到當前連結串列中(後向新增)
    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);//判斷index是否超出界

        Object[] a = c.toArray();//將集合轉換為陣列
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {//如果index為元素個數,即index個結點為尾結點
            succ = null;
            pred = last;//指向為結點
        } else {
            succ = node(index); //succ指向第index個結點
            pred = succ.prev;   //pred指向succ的前向結點
        }

        //for迴圈結束後,a裡面的元素都新增到當前連結串列裡面,後向新增
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //新生成一個結點,結點的前向指標指向pred,後向指標為null
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                //如果pred為null,則succ為當前頭結點
                first = newNode;
            else
                //pred的後向指標指向新結點
                pred.next = newNode;
            pred = newNode;//pred移動到新結點
        }

        if (succ == null) {
            last = pred;//succ為null,這表示index為尾結點之後
        } else {
            //pred表示所有元素新增之後的最後結點,此時pred的後向指標指向之前的記錄的結點
            pred.next = succ;
            succ.prev = pred;//之前記錄的結點指向新增元素之後的最後結點
        }

        size += numNew;//元素個數+num
        modCount++;//修改次數+1
        return true;
    }

    //清除連結串列裡面的所有元素
    public void clear() {
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;  //釋放值結點,便於GC回收
            x.next = null;  //釋放前向指標
            x.prev = null;  //釋放後向指標
            x = next;   //後向遍歷
        }
        first = last = null;//釋放頭尾結點
        size = 0;
        modCount++;
    }


    //獲得第index個結點的值
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;    //點位第index結點,返回值資訊
    }

    //設定第index元素的值
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);//定位第index個結點
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

    //第index個結點之前新增結點
    public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

    //刪除第index個結點
    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

    //判斷index是否是連結串列中的元素的索引
    private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }

    //判斷index是否是連結串列中的元素的索引
    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));
    }

    //定位連結串列中的第index個結點
    Node<E> node(int index) {
        // assert isElementIndex(index);//確保是合法的索引,即0<=index<=size
        //index小於size的一半時,從頭向後找
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//index大於size的一半時,從尾向前找
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

    //定位元素,首次出現的元素的值為o的結點序號
    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;
    }

    //定位元素,最後一次出現的元素值為o的元素序號
    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;
    }

    //實現佇列操作,返回第一個元素的值
    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);
    }

    //新增頭結點
    public boolean offerFirst(E e) {
        addFirst(e);
        return true;
    }

    //新增尾結點
    public boolean offerLast(E e) {
        addLast(e);
        return true;
    }

    //返回頭結點的值
    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;
    }

    //彈出第一個結點
    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);
    }

    //新增頭部結點
    public void push(E e) {
        addFirst(e);
    }

    //彈出第一個結點
    public E pop() {
        return removeFirst();
    }

    //刪除值為o的結點
    public boolean removeFirstOccurrence(Object o) {
        return remove(o);
    }

    //刪除值為o的結點(從尾部遍歷)
    public boolean removeLastOccurrence(Object o) {
        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;
    }

    //返回雙向迭代器
    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;//當前結點的後向結點
        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++;//結點序號+1
            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++;
        }
        // Lambda表示式結合迭代器進行遍歷
        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();
        }
    }

    private static class Node<E> {
        E item; //結點的值
        Node<E> next;   //結點的後向指標
        Node<E> prev;   //結點的前向指標

        //構造方法中已完成Node成員的賦值
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;    //結點的值賦值為element
            this.next = next;       //後向指標賦值
            this.prev = prev;       //前向指標賦值
        }
    }

    //返回前向迭代器
    public Iterator<E> descendingIterator() {
        return new DescendingIterator();
    }

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

    @SuppressWarnings("unchecked")
    private LinkedList<E> superClone() {
        try {
            return (LinkedList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }

    //拷貝操作,執行淺拷貝,只複製引用,而沒有複製引用指向的記憶體
    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;
    }

    //轉換為陣列
    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;
    }

    //轉換為陣列
    @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;
    }

    //序列化版本
    private static final long serialVersionUID = 876323262645176354L;

    //序列化
    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);
    }

    //反序列化
    @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());
    }

    //獲取一個分割器,1.8新增
    @Override
    public Spliterator<E> spliterator() {
        return new LLSpliterator<E>(this, -1, 0);
    }

    /**
     * A customized variant of Spliterators.IteratorSpliterator
     */
    static final class LLSpliterator<E> implements Spliterator<E> {
        ···
    }

}
複製程式碼

總結

  關於LinkedList的原始碼,給出幾點比較重要的總結:
  1.從原始碼中很明顯可以看出,LinkedList的實現是基於雙向連結串列的,且頭結點中不存放資料。
  2.注意兩個不同的構造方法。無參構造方法直接建立一個僅包含head節點的空連結串列;包含Collection的構造方法,先呼叫無參構造方法建立一個空連結串列,而後將Collection中的資料加入到連結串列的尾部後面。
  3.在查詢和刪除某元素時,原始碼中都劃分為該元素為null和不為null兩種情況來處理,LinkedList中允許元素為null。
  4.LinkedList是基於連結串列實現的,因此不存在容量不足的問題,所以這裡沒有擴容的方法。
  5.Node node(int index)方法。該方法返回雙向連結串列中指定位置處的節點,而連結串列中是沒有下標索引的,要指定位置出的元素,就要遍歷該連結串列,從原始碼的實現中,我們看到這裡有一個加速動作。原始碼中先將index與長度size的一半比較,如果index<(size<<1),就只從位置0往後遍歷到位置index處,而如果index>(size<<1),就只從位置size往前遍歷到位置index處。這樣可以減少一部分不必要的遍歷,從而提高一定的效率(實際上效率還是很低)。
  6.LinkedList是基於連結串列實現的,因此插入刪除效率高,查詢效率低(雖然有一個加速動作)。
  7.要注意原始碼中還實現了棧和佇列的操作方法,因此也可以作為棧、佇列和雙端佇列來使用。

參考

資料結構(二):連結串列
LearningNotes

相關文章