JDK中LinkedList的實現分析

LinkedList

JDK中的LinkedList是繼承自AbstractSequentialList,並實現了List、Deque、Queue等接口,並支持拷貝和序列化。

public class LinkedList<E> extends AbstractSequentialList<E> implements
    List<E>, Deque<E>, Queue<E>, Cloneable, Serializable 

先來閱讀以下注釋,瞭解一下概況:

LinkedList是List的一個實現,基於雙向鏈表結構。所有的操作,包括添加,刪除,元素替換都是支持的。
支持所有的元素,包括null。
這個類在你需要類似隊列的行爲的時候會用到。也會在你期望你的列表包含零個或者一個元素,但是又能擴展到更大數量的元素的時候。一般來說,你在不需要使用隊列類似的功能的時候,最好使用ArrayList。

Link類是一個內部靜態類,包括前驅和後繼以及數據,實現了鏈表節點的功能。

LinkIterator是一個實現ListIterator接口的內部靜態類,實現了鏈表的遍歷功能。

ReverseLinkIterator是一個實現了Iterator接口的內部類,實現了逆序遍歷的功能。

LinkedList使用一個叫做voidLink的空節點記錄鏈表的頭指針和尾指針,voidLink.previous是尾指針,voidLink.next是頭指針。在沒有結點時,鏈表的頭尾都指向voidLink自身。

LinkedList包含以下幾個主要操作的函數add、addAll、contains、get、indexOf、remove、push、pop、set等。下面一一來看看它們的實現。

首先是add,函數實現如下:

public void add(int location, E object) {
    if (location >= 0 && location <= size) {
        Link<E> link = voidLink;
        if (location < (size / 2)) {
            for (int i = 0; i <= location; i++) {
                link = link.next;
            }
        } else {
            for (int i = size; i > location; i--) {
                link = link.previous;
            }
        }
        Link<E> previous = link.previous;
        Link<E> newLink = new Link<E>(object, previous, link);
        previous.next = newLink;
        link.previous = newLink;
        size++;
        modCount++;
    } else {
        throw new IndexOutOfBoundsException();
    }
}

add函數可以實現在指定位置添加指定的對象數據。如果位置超出範圍,會拋出IndexOutOfBoundsException異常。
如果插入的位置location在鏈表的前半部分,那麼就從前面開始遍歷到該位置;如果location在鏈表的後半部分,就從鏈表的尾部開始往前遍歷到該位置。LinkedList之後通過該對象數據創建一個節點,並將節點插入到鏈表中,並更新前驅和後繼結點指針,以及鏈表長度。

不帶插入位置參數location的add函數則直接將對象數據插入到鏈表尾部。之後更新voidLink的鏈表的頭尾指針。

addAll函數將一個Collection集合中的所有對象數據都插入到指定位置,實現過程與add函數基本一致,只不過是連續插入多個元素。

addFirst函數則是創建新結點並設置前驅後繼,然後更新頭指針,更新原來頭指針的前驅,代碼如下:

public void addFirst(E object) {
    addFirstImpl(object);
}

private boolean addFirstImpl(E object) {
    Link<E> oldFirst = voidLink.next;
    Link<E> newLink = new Link<E>(object, voidLink, oldFirst);
    voidLink.next = newLink;
    oldFirst.previous = newLink;
    size++;
    modCount++;
    return true;
}

addLast函數與addFirst函數實現類似。

contains函數用於搜索鏈表是否包含某個指定的對象。首先判斷該對象是否爲空,如果不爲空,則從前向後遍歷該鏈表進行元素對比查找,直到找到元素或者鏈表遍歷完爲止;如果元素爲空,則遍歷鏈表查找第一個爲空的結點。

get函數用於返回指定位置的結點數據。如果位置在鏈表長度允許範圍之內,則根據位置在前半部分或者後半部分的情況,從頭指針往後或從尾指針往前進行查找。如果位置超出範圍,拋出IndexOutOfBoundsException異常。

indexOf函數用於查找指定對象數據在鏈表中的位置。實現方法是從前往後遍歷鏈表進行對象查找,直到找到對象數據或者遍歷完鏈表。如果找到就返回位置,否則返回-1。
lastIndexOf函數與indexOf類似,但是不一樣的是它返回的是最後一次出現的位置。因此,遍歷方向需要變化,即變成從後往前進行鏈表的遍歷。

remove函數用於刪除指定位置的結點。先判斷位置在前半部分還是後半部分來確定遍歷方向,然後更新結點指針即可。另外該函數需要返回該位置的對象數據。如果位置超出範圍,也會拋出IndexOutOfBoundsException異常。

public E remove(int location) {
    if (location >= 0 && location < size) {
        Link<E> link = voidLink;
        if (location < (size / 2)) {
            for (int i = 0; i <= location; i++) {
                link = link.next;
            }
        } else {
            for (int i = size; i > location; i--) {
                link = link.previous;
            }
        }
        Link<E> previous = link.previous;
        Link<E> next = link.next;
        previous.next = next;
        next.previous = previous;
        size--;
        modCount++;
        return link.data;
    }
    throw new IndexOutOfBoundsException();
}

另外pop與push是實現了棧的功能,這兩個函數是通過刪除隊首元素和在隊尾添加元素實現的。

set函數用於替換指定位置的結點中的對象數據。只需遍歷到位置並替換數據即可。

    public E set(int location, E object) {
    if (location >= 0 && location < size) {
        Link<E> link = voidLink;
        if (location < (size / 2)) {
            for (int i = 0; i <= location; i++) {
                link = link.next;
            }
        } else {
            for (int i = size; i > location; i--) {
                link = link.previous;
            }
        }
        E result = link.data;
        link.data = object;
        return result;
    }
    throw new IndexOutOfBoundsException();
}
發佈了204 篇原創文章 · 獲贊 280 · 訪問量 85萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章