1. 程式人生 > >java集合之LinkedList詳解

java集合之LinkedList詳解

我們上一次說到List的ArrayList,我們這次去看下LinkedList---顧名思義是連結串列,連結串列的優點就不用說了吧,增刪效率比較高(具體的朋友們上網看吧),先來看下LinkedList的整體構架:

 首先我們看到了LinkedList間接的實現了List介面(說明LinkedList是有list的特性的,add,remove等)、實現了Cloneable(可複製)、Serializable(進行了序列化),除此之外還有一個東西還實現了Queue(佇列,說明應該是有佇列的一些特性,pop等),這次先不側重queue的東西,我們主要看LinkedList是如何實現連結串列的。

    //連結串列的長度
    transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    //連結串列的頭
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    //連結串列的尾
    transient Node<E> last;
    //連結串列維護的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;
        }
    }

我們可以看到,我們連結串列維護了一個size、first(頭)和last(尾),我們可以看到Node的結構中有個item、next、prev充分說明了此連結串列是一個雙向連結串列。

    public static void main(String[] args){
        //初始化linkedList
        List<String> linkedList = new LinkedList<>();
        //新增元素
        linkedList.add("abc");
        linkedList.add("bcd");
        return;
    }

這個空的建構函式沒有什麼內容,所以我們就跳過去,我們直接看add方法具體做的什麼。

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #addLast}.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    //看方法註釋,我們可以看到是將該元素新增到此連結串列的end,返回true
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

    /**
     * Links e as last element.
     */
    void linkLast(E e) {

        //首先取出last節點
        final Node<E> l = last;

        //將newNode的prev指向last,後節點指向null,因為newNode永遠都是作為last的,所以next指向null
        final Node<E> newNode = new Node<>(l, e, null);

        //last指向newNode
        last = newNode;

        //如果連結串列為null的話則first也指向newNode,此時連結串列只有一個節點first和last都指向一個
        //如果連結串列部位null,只需要將last的next指向這個newNode就行了,這樣就形成了雙向連結串列
        if (l == null)
            first = newNode;
        else
            l.next = newNode;

        //最後size++
        size++;
        modCount++;
    }

LinkedList就是這樣一個節點一個節點關聯起來的,我們可以看一下下面的圖片(這裡偷個懶,網上隨便找了一張):

 我們接下來看一下remove(int index)刪除指定位置物件的方法,其中個人覺得有個有趣的地方,所以給大家分享下:

    /**
     * Removes the element at the specified position in this list.  Shifts any
     * subsequent elements to the left (subtracts one from their indices).
     * Returns the element that was removed from the list.
     *
     * @param index the index of the element to be removed
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        //檢查index是否超出範圍size
        checkElementIndex(index);
        //刪除指定位置的節點,首先得找到這個節點
        return unlink(node(index));
    }
    //檢查index是否正確
    private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * Returns the (non-null) Node at the specified element index.
     */
    //返回指定index位置的節點
    Node<E> node(int index) {
        // assert isElementIndex(index);
        
        //首先去比較index和size >> 1(也就是size的一半),如果比中間數小則從連結串列頭找,否則從尾找
        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;
        }
    }

我們可以看到,我們想要移除指定位置的節點,首先得找到這個節點(這個是重點),關鍵我們的連結串列如何找到指定位置?不知道大家有沒有自己比較好的方法,我們來看下jdk的思路(我覺得jdk的實現方法很有趣),node(int index)方法中的if條件拿index和size的一半比較,文字解釋太麻煩了,我來畫個圖吧:

假設:我們linkedList的長度為6,如要remove的index為3,此時我們3 < (size >> 1)為false,則我們從linkedList後面開始直到變數i>index,此時i變數位置的就是index位置的node,如果我們index是2,那麼是從前面開始的,我們大家想這樣其實是把linkedList從中間分為2個,速度是不是比沒分之前快兩倍呢,哈哈哈~

總結:linkedList為雙鏈表,維護的是一個first和last指標,而每個節點有item自身、prev和next兩個節點來維護雙鏈表的關係,其他的功能都是圍繞我們的雙鏈表來進行的,有興趣的大家可以仔細研究一下原始碼,有時會發現很有趣的小細節哦~