Collection容器初探之LinkedList

網上的LinkedList的源碼分析大都爲JDK1.6的版本,所以爲雙向循環鏈表的環形結構。但是JDK1.7的版本爲雙向鏈表。

先簡單分析瞭解下JDK1.6版本的源碼:

參考:

給jdk寫註釋系列之jdk1.6容器(2)-LinkedList源碼解析
這篇博客分析的很徹底!!!贊。


JDK1.6~JDK1.7過程中,LinkedList從雙向循環鏈表轉換爲雙向鏈表

這次來簡單瞭解和分析下JDK1.6的基於雙向循環鏈表的實現:

這裏寫圖片描述

繼承自List,Deque,Cloneable,Serializible接口



public class LinkedList
    extends AbstractSequentialList
    implements List, Deque, Cloneable, java.io.Serializable


雙向循環鏈表的底層數據存儲形式:


 private transient Entry header = new Entry(null, null, null);
    private transient int size = 0;

其中size爲元素數目,header爲鏈表的頭節點,Entry爲鏈表中的節點對象

經常使用的默認構造方法:


  public LinkedList() {
   //將header節點的前一節點和後一節點都設置爲自身
        header.next = header.previous = header;
    }


Entry爲LinkedList的靜態內部類,定義了當前存儲的節點和它相鄰的兩個節點:



    private static class Entry {
    E element;
    Entry next;
    Entry previous;

    Entry(E element, Entry next, Entry previous) {
        this.element = element;
        this.next = next;
        this.previous = previous;
    }
    }


插入:

這裏寫圖片描述

    /**
     * 添加一個集合元素到list中
     */
    public boolean addAll(Collection<? extends E> c) {
            // 將集合元素添加到list最後的尾部
        return addAll(size , c);
    }

    /**
     * 在指定位置添加一個集合元素到list中
     */
    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++;

        // 找出要插入元素的前後節點
        // 獲取要插入index位置的下一個節點,如果index正好是lsit尾部的位置那麼下一個節點就是header,否則需要查找index位置的節點
        Entry<E> successor = (index== size ? header : entry(index));
        // 獲取要插入index位置的上一個節點,因爲是插入,所以上一個點擊就是未插入前下一個節點的上一個
        Entry<E> predecessor = successor. previous;
        // 循環插入
        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;

        // 修改計數器
        size += numNew;
        return true;
    }

刪除:



/**
     * 刪除第一個匹配的指定元素
     */
    public boolean remove(Object o) {
         // 遍歷鏈表找到要被刪除的節點
        if (o==null) {
            for (Entry e = header .next; e != header; e = e.next ) {
                if (e.element ==null) {
                    remove(e);
                    return true;
                }
            }
        } else {
            for (Entry e = header .next; e != header; e = e.next ) {
                if (o.equals(e.element )) {
                    remove(e);
                    return true;
                }
            }
        }
        return false;
    }

    private E remove(Entry e) {
        if (e == header )
           throw new NoSuchElementException();

       // 被刪除的元素,供返回
        E result = e. element;
       // 下面修正前後對該節點的引用
       // 將該節點的上一個節點的next指向該節點的下一個節點
       e. previous.next = e.next;
       // 將該節點的下一個節點的previous指向該節點的上一個節點
       e. next.previous = e.previous;
       // 修正該節點自身的前後引用
        e. next = e.previous = null;
       // 將自身置空,讓gc可以儘快回收
        e. element = null;
       // 計數器減一
        size--;
        modCount++;
        return result;
    }


查詢:

/**
     * 查找指定索引位置的元素
     */
    public E get( int index) {
        return entry(index).element ;
    }

    /**
     * 返回指定索引位置的節點
     */
    private Entry<E> entry( int index) {
        // 越界檢查
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException( "Index: "+index+
                                                ", Size: "+size );
        // 取出頭結點
        Entry<E> e = header;
        // size>>1右移一位代表除以2,這裏使用簡單的二分方法,判斷index與list的中間位置的距離
        if (index < (size >> 1)) {
            // 如果index距離list中間位置較近,則從頭部向後遍歷(next)
            for (int i = 0; i <= index; i++)
                e = e. next;
        } else {
            // 如果index距離list中間位置較遠,則從頭部向前遍歷(previous)
            for (int i = size; i > index; i--)
                e = e. previous;
        }
        return e;
    }

改動:



/**
     * 修改指定位置索引位置的元素
     */
    public E set( int index, E element) {
        // 查找index位置的節點
        Entry e = entry(index);
        // 取出該節點的元素,供返回使用
        E oldVal = e. element;
        // 用新元素替換舊元素
        e. element = element;
        // 返回舊元素
        return oldVal;
    }



發佈了72 篇原創文章 · 獲贊 22 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章