【Java集合原始碼剖析】LinkedList原始碼剖析

蘭亭風雨發表於2014-07-01

轉載請註明出處:http://blog.csdn.net/ns_code/article/details/35787253


您好,我正在參加CSDN博文大賽,如果您喜歡我的文章,希望您能幫我投一票,謝謝!

投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011


LinkedList簡介

    LinkedList是基於雙向迴圈連結串列(從原始碼中可以很容易看出)實現的,除了可以當做連結串列來操作外,它還可以當做棧、佇列和雙端佇列來使用。

    LinkedList同樣是非執行緒安全的,只在單執行緒下適合使用。

    LinkedList實現了Serializable介面,因此它支援序列化,能夠通過序列化傳輸,實現了Cloneable介面,能被克隆。

LinkedList原始碼剖析

    LinkedList的原始碼如下(加入了比較詳細的註釋):

package java.util;  
 
public class LinkedList<E>  
    extends AbstractSequentialList<E>  
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable  
{  
    // 連結串列的表頭,表頭不包含任何資料。Entry是個連結串列類資料結構。  
    private transient Entry<E> header = new Entry<E>(null, null, null);  
 
    // LinkedList中元素個數  
    private transient int size = 0;  
 
    // 預設建構函式:建立一個空的連結串列  
    public LinkedList() {  
        header.next = header.previous = header;  
    }  
 
    // 包含“集合”的建構函式:建立一個包含“集合”的LinkedList  
    public LinkedList(Collection<? extends E> c) {  
        this();  
        addAll(c);  
    }  
 
    // 獲取LinkedList的第一個元素  
    public E getFirst() {  
        if (size==0)  
            throw new NoSuchElementException();  
 
        // 連結串列的表頭header中不包含資料。  
        // 這裡返回header所指下一個節點所包含的資料。  
        return header.next.element;  
    }  
 
    // 獲取LinkedList的最後一個元素  
    public E getLast()  {  
        if (size==0)  
            throw new NoSuchElementException();  
 
        // 由於LinkedList是雙向連結串列;而表頭header不包含資料。  
        // 因而,這裡返回表頭header的前一個節點所包含的資料。  
        return header.previous.element;  
    }  
 
    // 刪除LinkedList的第一個元素  
    public E removeFirst() {  
        return remove(header.next);  
    }  
 
    // 刪除LinkedList的最後一個元素  
    public E removeLast() {  
        return remove(header.previous);  
    }  
 
    // 將元素新增到LinkedList的起始位置  
    public void addFirst(E e) {  
        addBefore(e, header.next);  
    }  
 
    // 將元素新增到LinkedList的結束位置  
    public void addLast(E e) {  
        addBefore(e, header);  
    }  
 
    // 判斷LinkedList是否包含元素(o)  
    public boolean contains(Object o) {  
        return indexOf(o) != -1;  
    }  
 
    // 返回LinkedList的大小  
    public int size() {  
        return size;  
    }  
 
    // 將元素(E)新增到LinkedList中  
    public boolean add(E e) {  
        // 將節點(節點資料是e)新增到表頭(header)之前。  
        // 即,將節點新增到雙向連結串列的末端。  
        addBefore(e, header);  
        return true;  
    }  
 
    // 從LinkedList中刪除元素(o)  
    // 從連結串列開始查詢,如存在元素(o)則刪除該元素並返回true;  
    // 否則,返回false。  
    public boolean remove(Object o) {  
        if (o==null) {  
            // 若o為null的刪除情況  
            for (Entry<E> e = header.next; e != header; e = e.next) {  
                if (e.element==null) {  
                    remove(e);  
                    return true;  
                }  
            }  
        } else {  
            // 若o不為null的刪除情況  
            for (Entry<E> e = header.next; e != header; e = e.next) {  
                if (o.equals(e.element)) {  
                    remove(e);  
                    return true;  
                }  
            }  
        }  
        return false;  
    }  
 
    // 將“集合(c)”新增到LinkedList中。  
    // 實際上,是從雙向連結串列的末尾開始,將“集合(c)”新增到雙向連結串列中。  
    public boolean addAll(Collection<? extends E> c) {  
        return addAll(size, c);  
    }  
 
    // 從雙向連結串列的index開始,將“集合(c)”新增到雙向連結串列中。  
    public boolean addAll(int index, Collection<? extends E> c) {  
        if (index < 0 || index > size)  
            throw new IndexOutOfBoundsException("Index: "+index+  
                                                ", Size: "+size);  
        Object[] a = c.toArray();  
        // 獲取集合的長度  
        int numNew = a.length;  
        if (numNew==0)  
            return false;  
        modCount++;  
 
        // 設定“當前要插入節點的後一個節點”  
        Entry<E> successor = (index==size ? header : entry(index));  
        // 設定“當前要插入節點的前一個節點”  
        Entry<E> predecessor = successor.previous;  
        // 將集合(c)全部插入雙向連結串列中  
        for (int i=0; i<numNew; i++) {  
            Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);  
            predecessor.next = e;  
            predecessor = e;  
        }  
        successor.previous = predecessor;  
 
        // 調整LinkedList的實際大小  
        size += numNew;  
        return true;  
    }  
 
    // 清空雙向連結串列  
    public void clear() {  
        Entry<E> e = header.next;  
        // 從表頭開始,逐個向後遍歷;對遍歷到的節點執行一下操作:  
        // (01) 設定前一個節點為null   
        // (02) 設定當前節點的內容為null   
        // (03) 設定後一個節點為“新的當前節點”  
        while (e != header) {  
            Entry<E> next = e.next;  
            e.next = e.previous = null;  
            e.element = null;  
            e = next;  
        }  
        header.next = header.previous = header;  
        // 設定大小為0  
        size = 0;  
        modCount++;  
    }  
 
    // 返回LinkedList指定位置的元素  
    public E get(int index) {  
        return entry(index).element;  
    }  
 
    // 設定index位置對應的節點的值為element  
    public E set(int index, E element) {  
        Entry<E> e = entry(index);  
        E oldVal = e.element;  
        e.element = element;  
        return oldVal;  
    }  
   
    // 在index前新增節點,且節點的值為element  
    public void add(int index, E element) {  
        addBefore(element, (index==size ? header : entry(index)));  
    }  
 
    // 刪除index位置的節點  
    public E remove(int index) {  
        return remove(entry(index));  
    }  
 
    // 獲取雙向連結串列中指定位置的節點  
    private Entry<E> entry(int index) {  
        if (index < 0 || index >= size)  
            throw new IndexOutOfBoundsException("Index: "+index+  
                                                ", Size: "+size);  
        Entry<E> e = header;  
        // 獲取index處的節點。  
        // 若index < 雙向連結串列長度的1/2,則從前先後查詢;  
        // 否則,從後向前查詢。  
        if (index < (size >> 1)) {  
            for (int i = 0; i <= index; i++)  
                e = e.next;  
        } else {  
            for (int i = size; i > index; i--)  
                e = e.previous;  
        }  
        return e;  
    }  
 
    // 從前向後查詢,返回“值為物件(o)的節點對應的索引”  
    // 不存在就返回-1  
    public int indexOf(Object o) {  
        int index = 0;  
        if (o==null) {  
            for (Entry e = header.next; e != header; e = e.next) {  
                if (e.element==null)  
                    return index;  
                index++;  
            }  
        } else {  
            for (Entry e = header.next; e != header; e = e.next) {  
                if (o.equals(e.element))  
                    return index;  
                index++;  
            }  
        }  
        return -1;  
    }  
 
    // 從後向前查詢,返回“值為物件(o)的節點對應的索引”  
    // 不存在就返回-1  
    public int lastIndexOf(Object o) {  
        int index = size;  
        if (o==null) {  
            for (Entry e = header.previous; e != header; e = e.previous) {  
                index--;  
                if (e.element==null)  
                    return index;  
            }  
        } else {  
            for (Entry e = header.previous; e != header; e = e.previous) {  
                index--;  
                if (o.equals(e.element))  
                    return index;  
            }  
        }  
        return -1;  
    }  
 
    // 返回第一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E peek() {  
        if (size==0)  
            return null;  
        return getFirst();  
    }  
 
    // 返回第一個節點  
    // 若LinkedList的大小為0,則丟擲異常  
    public E element() {  
        return getFirst();  
    }  
 
    // 刪除並返回第一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E poll() {  
        if (size==0)  
            return null;  
        return removeFirst();  
    }  
 
    // 將e新增雙向連結串列末尾  
    public boolean offer(E e) {  
        return add(e);  
    }  
 
    // 將e新增雙向連結串列開頭  
    public boolean offerFirst(E e) {  
        addFirst(e);  
        return true;  
    }  
 
    // 將e新增雙向連結串列末尾  
    public boolean offerLast(E e) {  
        addLast(e);  
        return true;  
    }  
 
    // 返回第一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E peekFirst() {  
        if (size==0)  
            return null;  
        return getFirst();  
    }  
 
    // 返回最後一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E peekLast() {  
        if (size==0)  
            return null;  
        return getLast();  
    }  
 
    // 刪除並返回第一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E pollFirst() {  
        if (size==0)  
            return null;  
        return removeFirst();  
    }  
 
    // 刪除並返回最後一個節點  
    // 若LinkedList的大小為0,則返回null  
    public E pollLast() {  
        if (size==0)  
            return null;  
        return removeLast();  
    }  
 
    // 將e插入到雙向連結串列開頭  
    public void push(E e) {  
        addFirst(e);  
    }  
 
    // 刪除並返回第一個節點  
    public E pop() {  
        return removeFirst();  
    }  
 
    // 從LinkedList開始向後查詢,刪除第一個值為元素(o)的節點  
    // 從連結串列開始查詢,如存在節點的值為元素(o)的節點,則刪除該節點  
    public boolean removeFirstOccurrence(Object o) {  
        return remove(o);  
    }  
 
    // 從LinkedList末尾向前查詢,刪除第一個值為元素(o)的節點  
    // 從連結串列開始查詢,如存在節點的值為元素(o)的節點,則刪除該節點  
    public boolean removeLastOccurrence(Object o) {  
        if (o==null) {  
            for (Entry<E> e = header.previous; e != header; e = e.previous) {  
                if (e.element==null) {  
                    remove(e);  
                    return true;  
                }  
            }  
        } else {  
            for (Entry<E> e = header.previous; e != header; e = e.previous) {  
                if (o.equals(e.element)) {  
                    remove(e);  
                    return true;  
                }  
            }  
        }  
        return false;  
    }  
 
    // 返回“index到末尾的全部節點”對應的ListIterator物件(List迭代器)  
    public ListIterator<E> listIterator(int index) {  
        return new ListItr(index);  
    }  
 
    // List迭代器  
    private class ListItr implements ListIterator<E> {  
        // 上一次返回的節點  
        private Entry<E> lastReturned = header;  
        // 下一個節點  
        private Entry<E> next;  
        // 下一個節點對應的索引值  
        private int nextIndex;  
        // 期望的改變計數。用來實現fail-fast機制。  
        private int expectedModCount = modCount;  
 
        // 建構函式。  
        // 從index位置開始進行迭代  
        ListItr(int index) {  
            // index的有效性處理  
            if (index < 0 || index > size)  
                throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size);  
            // 若 “index 小於 ‘雙向連結串列長度的一半’”,則從第一個元素開始往後查詢;  
            // 否則,從最後一個元素往前查詢。  
            if (index < (size >> 1)) {  
                next = header.next;  
                for (nextIndex=0; nextIndex<index; nextIndex++)  
                    next = next.next;  
            } else {  
                next = header;  
                for (nextIndex=size; nextIndex>index; nextIndex--)  
                    next = next.previous;  
            }  
        }  
 
        // 是否存在下一個元素  
        public boolean hasNext() {  
            // 通過元素索引是否等於“雙向連結串列大小”來判斷是否達到最後。  
            return nextIndex != size;  
        }  
 
        // 獲取下一個元素  
        public E next() {  
            checkForComodification();  
            if (nextIndex == size)  
                throw new NoSuchElementException();  
 
            lastReturned = next;  
            // next指向連結串列的下一個元素  
            next = next.next;  
            nextIndex++;  
            return lastReturned.element;  
        }  
 
        // 是否存在上一個元素  
        public boolean hasPrevious() {  
            // 通過元素索引是否等於0,來判斷是否達到開頭。  
            return nextIndex != 0;  
        }  
 
        // 獲取上一個元素  
        public E previous() {  
            if (nextIndex == 0)  
            throw new NoSuchElementException();  
 
            // next指向連結串列的上一個元素  
            lastReturned = next = next.previous;  
            nextIndex--;  
            checkForComodification();  
            return lastReturned.element;  
        }  
 
        // 獲取下一個元素的索引  
        public int nextIndex() {  
            return nextIndex;  
        }  
 
        // 獲取上一個元素的索引  
        public int previousIndex() {  
            return nextIndex-1;  
        }  
 
        // 刪除當前元素。  
        // 刪除雙向連結串列中的當前節點  
        public void remove() {  
            checkForComodification();  
            Entry<E> lastNext = lastReturned.next;  
            try {  
                LinkedList.this.remove(lastReturned);  
            } catch (NoSuchElementException e) {  
                throw new IllegalStateException();  
            }  
            if (next==lastReturned)  
                next = lastNext;  
            else 
                nextIndex--;  
            lastReturned = header;  
            expectedModCount++;  
        }  
 
        // 設定當前節點為e  
        public void set(E e) {  
            if (lastReturned == header)  
                throw new IllegalStateException();  
            checkForComodification();  
            lastReturned.element = e;  
        }  
 
        // 將e新增到當前節點的前面  
        public void add(E e) {  
            checkForComodification();  
            lastReturned = header;  
            addBefore(e, next);  
            nextIndex++;  
            expectedModCount++;  
        }  
 
        // 判斷 “modCount和expectedModCount是否相等”,依次來實現fail-fast機制。  
        final void checkForComodification() {  
            if (modCount != expectedModCount)  
            throw new ConcurrentModificationException();  
        }  
    }  
 
    // 雙向連結串列的節點所對應的資料結構。  
    // 包含3部分:上一節點,下一節點,當前節點值。  
    private static class Entry<E> {  
        // 當前節點所包含的值  
        E element;  
        // 下一個節點  
        Entry<E> next;  
        // 上一個節點  
        Entry<E> previous;  
 
        /**  
         * 連結串列節點的建構函式。  
         * 引數說明:  
         *   element  —— 節點所包含的資料  
         *   next      —— 下一個節點  
         *   previous —— 上一個節點  
         */ 
        Entry(E element, Entry<E> next, Entry<E> previous) {  
            this.element = element;  
            this.next = next;  
            this.previous = previous;  
        }  
    }  
 
    // 將節點(節點資料是e)新增到entry節點之前。  
    private Entry<E> addBefore(E e, Entry<E> entry) {  
        // 新建節點newEntry,將newEntry插入到節點e之前;並且設定newEntry的資料是e  
        Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);  
        newEntry.previous.next = newEntry;  
        newEntry.next.previous = newEntry;  
        // 修改LinkedList大小  
        size++;  
        // 修改LinkedList的修改統計數:用來實現fail-fast機制。  
        modCount++;  
        return newEntry;  
    }  
 
    // 將節點從連結串列中刪除  
    private E remove(Entry<E> e) {  
        if (e == header)  
            throw new NoSuchElementException();  
 
        E result = e.element;  
        e.previous.next = e.next;  
        e.next.previous = e.previous;  
        e.next = e.previous = null;  
        e.element = null;  
        size--;  
        modCount++;  
        return result;  
    }  
 
    // 反向迭代器  
    public Iterator<E> descendingIterator() {  
        return new DescendingIterator();  
    }  
 
    // 反向迭代器實現類。  
    private class DescendingIterator implements Iterator {  
        final ListItr itr = new ListItr(size());  
        // 反向迭代器是否下一個元素。  
        // 實際上是判斷雙向連結串列的當前節點是否達到開頭  
        public boolean hasNext() {  
            return itr.hasPrevious();  
        }  
        // 反向迭代器獲取下一個元素。  
        // 實際上是獲取雙向連結串列的前一個節點  
        public E next() {  
            return itr.previous();  
        }  
        // 刪除當前節點  
        public void remove() {  
            itr.remove();  
        }  
    }  
 
 
    // 返回LinkedList的Object[]陣列  
    public Object[] toArray() {  
    // 新建Object[]陣列  
    Object[] result = new Object[size];  
        int i = 0;  
        // 將連結串列中所有節點的資料都新增到Object[]陣列中  
        for (Entry<E> e = header.next; e != header; e = e.next)  
            result[i++] = e.element;  
    return result;  
    }  
 
    // 返回LinkedList的模板陣列。所謂模板陣列,即可以將T設為任意的資料型別  
    public <T> T[] toArray(T[] a) {  
        // 若陣列a的大小 < LinkedList的元素個數(意味著陣列a不能容納LinkedList中全部元素)  
        // 則新建一個T[]陣列,T[]的大小為LinkedList大小,並將該T[]賦值給a。  
        if (a.length < size)  
            a = (T[])java.lang.reflect.Array.newInstance(  
                                a.getClass().getComponentType(), size);  
        // 將連結串列中所有節點的資料都新增到陣列a中  
        int i = 0;  
        Object[] result = a;  
        for (Entry<E> e = header.next; e != header; e = e.next)  
            result[i++] = e.element;  
 
        if (a.length > size)  
            a[size] = null;  
 
        return a;  
    }  
 
 
    // 克隆函式。返回LinkedList的克隆物件。  
    public Object clone() {  
        LinkedList<E> clone = null;  
        // 克隆一個LinkedList克隆物件  
        try {  
            clone = (LinkedList<E>) super.clone();  
        } catch (CloneNotSupportedException e) {  
            throw new InternalError();  
        }  
 
        // 新建LinkedList表頭節點  
        clone.header = new Entry<E>(null, null, null);  
        clone.header.next = clone.header.previous = clone.header;  
        clone.size = 0;  
        clone.modCount = 0;  
 
        // 將連結串列中所有節點的資料都新增到克隆物件中  
        for (Entry<E> e = header.next; e != header; e = e.next)  
            clone.add(e.element);  
 
        return clone;  
    }  
 
    // java.io.Serializable的寫入函式  
    // 將LinkedList的“容量,所有的元素值”都寫入到輸出流中  
    private void writeObject(java.io.ObjectOutputStream s)  
        throws java.io.IOException {  
        // Write out any hidden serialization magic  
        s.defaultWriteObject();  
 
        // 寫入“容量”  
        s.writeInt(size);  
 
        // 將連結串列中所有節點的資料都寫入到輸出流中  
        for (Entry e = header.next; e != header; e = e.next)  
            s.writeObject(e.element);  
    }  
 
    // java.io.Serializable的讀取函式:根據寫入方式反向讀出  
    // 先將LinkedList的“容量”讀出,然後將“所有的元素值”讀出  
    private void readObject(java.io.ObjectInputStream s)  
        throws java.io.IOException, ClassNotFoundException {  
        // Read in any hidden serialization magic  
        s.defaultReadObject();  
 
        // 從輸入流中讀取“容量”  
        int size = s.readInt();  
 
        // 新建連結串列表頭節點  
        header = new Entry<E>(null, null, null);  
        header.next = header.previous = header;  
 
        // 從輸入流中將“所有的元素值”並逐個新增到連結串列中  
        for (int i=0; i<size; i++)  
            addBefore((E)s.readObject(), header);  
    }  
 
} 

幾點總結

    關於LinkedList的原始碼,給出幾點比較重要的總結:

    1、從原始碼中很明顯可以看出,LinkedList的實現是基於雙向迴圈連結串列的,且頭結點中不存放資料,如下圖;


    2、注意兩個不同的構造方法。無參構造方法直接建立一個僅包含head節點的空連結串列,包含Collection的構造方法,先呼叫無參構造方法建立一個空連結串列,而後將Collection中的資料加入到連結串列的尾部後面。

    3、在查詢和刪除某元素時,原始碼中都劃分為該元素為null和不為null兩種情況來處理,LinkedList中允許元素為null。

    4、LinkedList是基於連結串列實現的,因此不存在容量不足的問題,所以這裡沒有擴容的方法。

    5、注意原始碼中的Entry<E> entry(int index)方法。該方法返回雙向連結串列中指定位置處的節點,而連結串列中是沒有下標索引的,要指定位置出的元素,就要遍歷該連結串列,從原始碼的實現中,我們看到這裡有一個加速動作原始碼中先將index與長度size的一半比較,如果index<size/2,就只從位置0往後遍歷到位置index處,而如果index>size/2,就只從位置size往前遍歷到位置index處。這樣可以減少一部分不必要的遍歷,從而提高一定的效率(實際上效率還是很低)。

    6、注意連結串列類對應的資料結構Entry。如下;

    // 雙向連結串列的節點所對應的資料結構。  
    // 包含3部分:上一節點,下一節點,當前節點值。  
    private static class Entry<E> {  
        // 當前節點所包含的值  
        E element;  
        // 下一個節點  
        Entry<E> next;  
        // 上一個節點  
        Entry<E> previous;  
 
        /**  
         * 連結串列節點的建構函式。  
         * 引數說明:  
         *   element  —— 節點所包含的資料  
         *   next      —— 下一個節點  
         *   previous —— 上一個節點  
         */ 
        Entry(E element, Entry<E> next, Entry<E> previous) {  
            this.element = element;  
            this.next = next;  
            this.previous = previous;  
        }  
    }  
    7、LinkedList是基於連結串列實現的,因此插入刪除效率高,查詢效率低(雖然有一個加速動作)
    8、要注意原始碼中還實現了棧和佇列的操作方法,因此也可以作為棧、佇列和雙端佇列來使用。

您好,我正在參加CSDN博文大賽,如果您喜歡我的文章,希望您能幫我投一票,謝謝!

投票地址:http://vote.blog.csdn.net/Article/Details?articleid=35568011

相關文章