集合的實現原理【二】

  • LinkedList中數據的載體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;
        }
    }

鏈表的這種托盤的結構決定了它的的特點:

  1. 數據的寫入很快,但是數據的讀取很慢。
  2. 數據沒有擴容的概念。

在鏈表中有兩個托盤:firstNode和lastNode。

    transient Node<E> first;
    transient Node<E> last;

在第一次添加元素時,新的元素既是firstNode也是lastNode,這點可以在源碼中體現。

  • 鏈表中添加(add)方法解析
    transient int size = 0;

    public void add(int index, E element) {
        checkPositionIndex(index);
        //checkPositionIndex方法是用來檢測index是否在[0,size)中,在返回index,否則拋出
        //IndexOutOfBoundsException異常。
        if (index == size)
            linkLast(element); //添加第一個元素
        else
            linkBefore(element, node(index)); //添加第N個元素。
    }
    
    

在add方法中判斷是不是第一次添加,size在這裏的默認值爲0,當第一次添加是index爲0,執行linkLast方法。

    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    /* 這裏的last變量是上文中的托盤 Node<E> last;
     * l, e, null 變量的意思是上家,數據元素,下家,這裏下家爲null.
     * 當l == null 時,設置first = newNode;這句話的意思就是
     * 【在第一次添加是】新元素既是第一個也是最後一個。
     */

在這裏體現了第一個添加的元素既是FirstNode也是LastNode這句話。

而下面這句話 l.next = newNode 體現了鏈表每個托盤是雙向關聯的。

當我們添加的數據不是第一個是,就會執行linkBefore方法,但是在分析linkBefore方法之前,我們先來分析一下 Node<E> node(int index)

這個方法:

    Node<E> node(int index) {
        if (index < (size >> 1)) { //判斷index在前半段還是在後半段。
            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;
        }
    }

這個方法是鏈表中的查詢方法,index在前半段時取出第一個托盤 Node<E> x = first ,往後查 x = x.next

當index在後半段時取出最後一個托盤 Node<E> x = last ,往前查 x = x.prev 。

當找到要插入的位置時,返回所在的托盤, linkBefore(E e, Node<E> succ)方法承接這個托盤,將新的數據插入到該節點之前。

    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }
    /* 首先獲取succ的上家記做pred,將pred當做上家傳入新創建的托盤newNode中,將succ當做下家傳入
     * newNode 中,最後形成 new Node<>(pred, e, succ)。
     * 如果pred不存在,那就把NowNode設置爲firstNode。否則,將
     * pred的下家設置爲newNode。
     */

以上總結是個人觀點,如有存在錯誤請指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章