Java集合類--LinkedList

轉自:http://www.cnblogs.com/huangfox/archive/2010/10/11/1847863.htmlhtml

1、 LinkedList
 安全

3.1      建立:LinkedList()數據結構

LinkedList底層的數據結構是一個雙向鏈表。既然是雙向鏈表,那麼一定存在一種數據結構——咱們能夠稱之爲節點,節點實例保存業務數據,前一個節點的位置信息和後一個節點位置信息,以下圖所示:函數

圖——雙線鏈表及節點示意圖this

 

首先來了解節點類:spa

private static class Entry{線程

    Eelement;指針

    Entry next;htm

    Entry previous;對象

 

    Entry(Eelement, Entrynext, Entryprevious) {

        this.element = element;

        this.next = next;

        this.previous = previous;

    }

    }

節點類很簡單,element存放業務數據,previous與next分別存放先後節點的信息(在數據結構中咱們一般稱之爲先後節點的指針)。

聲明LinkedList對象時,建立一個Entry對象,不過所有爲空。

private transient Entry header = new Entry(null, null, null);

private transient int size = 0;

在執行構造函數的時候,將header實例的previous和next所有指向header實例。

public LinkedList(){

       header.next = header.previous = header;

    }

執行完構造函數後,header實例自身造成一個閉環,以下圖所示:

圖——初始化LinkedList

 

3.2      添加數據:add()

從源代碼中咱們可知——給LinkedList添加數據是從雙向鏈表的頭開始的,代碼以下所示:

public boolean add(E e) {

    addBefore(e, header);

       return true;

    }

private Entry addBefore(Ee, Entry entry) {

    Entry newEntry = new Entry(e, entry, entry.previous);

    newEntry.previous.next =newEntry;

    newEntry.next.previous =newEntry;

    size++;

    modCount++;

    return newEntry;

    }

下面分解「添加第一個數據」的步驟:

第一步:初始化後LinkedList實例的狀況:

圖——初始化後

 

第二步:初始化一個預添加的Entry實例(newEntry)。

Entry newEntry = newEntry(e, entry, entry.previous);

圖——建立新節點實例

 

第三步:調整新加入節點和頭結點(header)的先後指針。

newEntry.previous.next = newEntry;

newEntry.previous即header,newEntry.previous.next即header的next指向newEntry實例。在上圖中應該是「4號線」指向newEntry。

newEntry.next.previous = newEntry;

newEntry.next即header,newEntry.next.previous即header的previous指向newEntry實例。在上圖中應該是「3號線」指向newEntry。

調整後以下圖所示:

圖——加入第一個節點後LinkedList示意圖

 

下面分解「添加第二個數據」的步驟:

第一步:新建節點。

圖——添加第二個節點

 

第二步:調整新節點和頭結點的先後指針信息。

圖——調整先後指針信息

 

添加後續數據狀況和上述一致,LinkedList實例是沒有容量限制的。

3.3      刪除數據:remove()remove(int)remove(Object)

LinkedList的數據刪除操做雖然有多種(這裏認爲只包括remove()、remove(int)、remove(Object) ),可是真正執行刪除操做的代碼以下所示:

private E remove(Entry 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;

    }

Remove方法接收的參數是一個節點實例,即預被刪除的節點。只不過remove()是刪除的第一個節點(頭結點後的第一個節點)——

public EremoveFirst() {

    return remove(header.next);

    }

Remove(int)是刪除指定位置的節點——

private Entry entry(int index) {

       if (index < 0 || index >= size)

           throw new IndexOutOfBoundsException("Index: "+index+

                                               ", Size: "+size);

       Entry e = header;

       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;

    }

Remove(Object)是刪除指定內容的節點——

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;

    }

在回到咱們的remove(Entry e)方法:

因爲刪除了某一節點所以調整相應節點的先後指針信息,以下:

e.previous.next = e.next;//預刪除節點的前一節點的後指針指向預刪除節點的後一個節點。 

e.next.previous = e.previous;//預刪除節點的後一節點的前指針指向預刪除節點的前一個節點。 

清空預刪除節點:

e.next = e.previous = null;

e.element = null;

交給gc完成資源回收,刪除操做結束。

與ArrayList比較而言,LinkedList的刪除動做不須要「移動」不少數據,從而效率更高。

3.4      獲取數據:get(int)

Get(int)方法的實如今remove(int)中已經涉及過了。首先判斷位置信息是否合法(大於等於0,小於當前LinkedList實例的Size),而後遍歷到具體位置,得到節點的業務數據(element)並返回。

注意:爲了提升效率,須要根據獲取的位置判斷是從頭仍是從尾開始遍歷。

private Entry entry(int index) {

       if (index < 0 || index >= size)

           throw new IndexOutOfBoundsException("Index: "+index+

                                               ", Size: "+size);

       Entry e = header;

       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;

    }

注意細節:位運算與直接作除法的區別。

3.5      遍歷數據:Iterator()

對ArrayList的iterator有所瞭解後,在此重點關注如下幾點:

當調用iterator方法是,每次都建立一個ListItr實例,它擁有一個屬性cursor,起到遊標的做用。

Iterator實例的hasNext方法:

public boolean hasNext() {

     return cursor != size();

}

當遊標跑到最後時返回false,說明遍歷完成。

Iterator實例的next方法:

public E next() {

           checkForComodification();

        try {

       Enext = get(cursor);

       lastRet= cursor++;

       return next;

        } catch (IndexOutOfBoundsException e) {

       checkForComodification();

       throw newNoSuchElementException();

        }

    }

經過get方法返回當前遊標位置的節點內容,並將遊標後移一個位置。

LinkedList在遍歷的過程當中,若是有添加、刪除等動做發生,會致使ConcurrentModificationException異常,和ArrayList相似。

3.6      判斷存在或獲取位置 

獲取位置的IndexOf方法在remove(Object)中已經涉及,而判斷存在的contains(Object)方法則主要是調用IndexOf方法,根據IndexOf返回的位置和-1進行比較進而判斷指定數據是否存在。

3.7      注意 

LinkedList是無容量限制的;

LinkedList是非線程安全的;

LinkedList是基於雙向鏈表實現的,當數據順序無關的狀況下,選擇ArrayList仍是LinkedList要從各動做的執行效率綜合考慮。

相關文章
相關標籤/搜索