【源碼】LinkedList源碼剖析

來源:互聯網
上載者:User

標籤:des   style   blog   http   color   java   使用   os   

//-----------------------------------------------------------轉載請註明出處:http://blog.csdn.net/chdjjby Rowandjj2014/8/8//----------------------------------------------------------
註:以下源碼基於jdk1.7.0_11

上一篇我們分析了ArrayList,今天我們再來看下LinkedList。

首先上一幅架構圖:


LinkedList同樣間接繼承了AbstractList抽象類別,對外來看,LinkedList提供的操作介面跟ArrayList是很類似的,差別在於內部實現上。稍微有點基礎的都知道,LinkedList是基於雙向鏈表這種資料結構,而ArrayList上一篇已經分析過了,是通過數組實現的。我們依舊按照之前的思路,自頂向下分析,AbstractList以及其上面的類或介面我們上一篇已經分析過,這裡不再重複,我們從AbstractSequentialList開始。
package java.util;public abstract class AbstractSequentialList<E> extends AbstractList<E> {    protected AbstractSequentialList() {//只有一個構造器    }   public E get(int index) {//擷取指定位置的值        try {            return listIterator(index).next();//通過迭代器的方式        } catch (NoSuchElementException exc) {//找不到就拋出異常            throw new IndexOutOfBoundsException("Index: "+index);//這是個Runtime異常        }    }    public E set(int index, E element) {        try {            ListIterator<E> e = listIterator(index);//同樣調用的listiterator            E oldVal = e.next();//記錄            e.set(element);            return oldVal;//返回        } catch (NoSuchElementException exc) {            throw new IndexOutOfBoundsException("Index: "+index);        }    }    public void add(int index, E element) {        try {            listIterator(index).add(element);        } catch (NoSuchElementException exc) {            throw new IndexOutOfBoundsException("Index: "+index);        }    }    public E remove(int index) {        try {            ListIterator<E> e = listIterator(index);            E outCast = e.next();            e.remove();            return outCast;        } catch (NoSuchElementException exc) {            throw new IndexOutOfBoundsException("Index: "+index);        }    }    // Bulk Operations    public boolean addAll(int index, Collection<? extends E> c) {        try {            boolean modified = false;            ListIterator<E> e1 = listIterator(index);            Iterator<? extends E> e2 = c.iterator();            while (e2.hasNext()) {                e1.add(e2.next());                modified = true;            }            return modified;        } catch (NoSuchElementException exc) {            throw new IndexOutOfBoundsException("Index: "+index);        }    }    // Iterators    public Iterator<E> iterator() {        return listIterator();    }    public abstract ListIterator<E> listIterator(int index);//參數為索引位置。表示從哪開始遍曆}
可以發現,這個抽象類別中的方法都依賴於這個ListIterator迭代器,而這個擷取迭代器的方法是抽象的,留給子類完成,另外iterator方法並沒有返回iterator,而同樣是返回了listiterator對象。
接下來,我們分析LinkedList。先看聲明:
public class LinkedList<E>    extends AbstractSequentialList<E>    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
需要注意的是LinkedList實現了Deque介面,這個介面代表一個雙端隊列,內部封裝了雙端隊列的所有操作,故而LinkedList可以當做一個棧、隊列或者是雙端隊列來使用。下面是其成員變數:
 transient int size = 0;//集合大小(結點個數) transient Node<E> first;//頭指標 transient Node<E> last;//尾指標
前面說過,linkedList是通過雙向鏈表實現,故而不需要有擴容的方法,因為結點是動態申請的。而這個結點的類型即為Node。下面看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;        }    }
很顯然是個雙向鏈表的結點結構。再看LinkedList構造器:
 public LinkedList() {} public LinkedList(Collection<? extends E> c) {        this();        addAll(c);    }
再看一些對結點的操作方法:如果你熟悉雙向鏈表,就會發現下面幾個函數很簡單,無非是處理指標的指向問題。
 private void linkFirst(E e) {//插到頭部        final Node<E> f = first;       //建立一個新結點,前驅為空白,後繼為f(也就是當前的頭結點)        final Node<E> newNode = new Node<>(null, e, f);//注意這種泛型的寫法也是可以的        first = newNode;//頭指標指向新結點        if (f == null)//鏈表為空白時            last = newNode;//尾指標指向新結點        else//否則            f.prev = newNode;//讓f的前驅指向新結點        size++;        modCount++;    }    void linkLast(E e) {//插到尾部        final Node<E> l = last;//臨時變數記錄尾結點        final Node<E> newNode = new Node<>(l, e, null);//建立新結點,前驅為l        last = newNode;//更新尾指標        if (l == null)//如果鏈表為空白            first = newNode;        else            l.next = newNode;        size++;        modCount++;//用於快速失敗機制    }    void linkBefore(E e, Node<E> succ) {//將e插入succ之前        // assert succ != null;//調用者需要保證succ不為空白        final Node<E> pred = succ.prev;//記錄succ的前驅        final Node<E> newNode = new Node<>(pred, e, succ);//新結點的前驅指向succ的前驅,新結點的後繼指向succ        succ.prev = newNode;//succ的前驅指向新結點        if (pred == null)//succ為頭結點            first = newNode;//更改頭指標        else            pred.next = newNode;//否則succ的前驅的後繼指向新結點        size++;        modCount++;    }    private E unlinkFirst(Node<E> f) {//幹迴轉結點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;    }    private E unlinkLast(Node<E> l) {//幹掉尾結點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;    }    E unlink(Node<E> x) {//幹掉一個普通結點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;    }
有了這些基本函數之後,實現其他動作就方便了。比如這些:
 public void addFirst(E e) {        linkFirst(e);    }   public void addLast(E e) {        linkLast(e);    }  public boolean add(E e) {        linkLast(e);        return true;    }
再看這個remove方法:

 public boolean remove(Object 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;    }

跟ArrayList類似,根據參數是否為null,進行了兩種處理,說明LinkedList也是支援null的元素的
再看清空操作:
 public void clear() {        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++;    }
下面的函數封裝了索引鏈表位置的操作:
 Node<E> node(int index) {        // assert isElementIndex(index);        if (index < (size >> 1)) {//判斷待索引的一般位置            Node<E> x = first;            for (int i = 0; i < index; i++)                x = x.next;            return x;        } else {            Node<E> x = last;            for (int i = size - 1; i > index; i--)                x = x.prev;            return x;        }    }
這個函數用了一個小技巧,首先判斷待索引的位置是在鏈表前半部分還是後半部分,若是前半部分,則順序索引,否則逆序索引(這就是雙向鏈表的優點之一)。之前在AbstractSequentialList中未實現的方法在這裡得到了實現:
 public ListIterator<E> listIterator(int index) {        checkPositionIndex(index);        return new ListItr(index);    }

這個ListItr是LinkedList的內部類,實現了ListIterator介面。

private class ListItr implements ListIterator<E>    private Node<E> next;  ListItr(int index) {            // assert isPositionIndex(index);            next = (index == size) ? null : node(index);            nextIndex = index;        }

通過構造器指定起始遍曆的位置,內部通過調用node方法索引該位置的對象。具體方法限於篇幅不在介紹。值得一提的是這個類還提供了一個反向的迭代器:
public Iterator<E> descendingIterator() {        return new DescendingIterator();    }

這個反向迭代器其實是對上面介紹的ListItr的封裝。
總結:1.LinkedList內部通過雙向鏈表實現;2.LinkedList支援null元素;3.LinkedList插入刪除元素較方便,但是尋找操作較耗時(對比ArrayList),雖然內部進行了最佳化(根據位置選擇順序還是逆序遍曆);4.LinkedList內部同樣通過內部類的形式實現了迭代器(僅實現了ListIterator,iterator方法返回的也是ListIterator對象)。5.LinkedList實現了Deque介面,可以當成棧、隊列、雙端隊列來使用。








聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.