Java從入門到放棄(八)集合框架之LinkedList源碼(1)

        LinkedList是基於雙向鏈表的集合,先看以下代碼:

        List<Integer> list = new LinkedList<>();
        for(int i = 1;i < 6;i++){
            list.add(i);
        }
代碼執行後數據如圖所示:
結合LinkedList源碼:
    transient int size = 0;

    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;
LinkedList的三個數據成員,size是集合內元素的個數,first指向頭節點,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;
        }
    }
包括三個成員變量,item是存儲的數據元素,next指向後一個Node節點,perv指向前一個Node節點。

1、構造方法

    只有兩個構造方法,一個無參構造,一個有參

    public LinkedList() {
    }
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

它和ArrayList不一樣,沒有指定初始化容量的構造函數。有參構造就是把集合全部加入到鏈表中,

2、add方法

 public boolean add(E e) {
        linkLast(e);
        return true;
    }
直接調用linkLast方法,看一下linkLast方法:
   void linkLast(E e) {
        final Node<E> l = last;     //把last節點賦值給l
        final Node<E> newNode = new Node<>(l, e, null);     //根據加入的元素構造新的node節點
        last = newNode;                                     //新的node節點設置爲last節點
        if (l == null)                                      //last節點不爲空的時候
            first = newNode;                                //即添加第一個節點的時候last爲null,就把新節點設置爲first
        else
            l.next = newNode;                               //把l節點(之前的last)的next指向新的node節點
        size++;                                             // 數據的size加1
        modCount++;                                         
    }

add方法就是把新節點設爲last節點,在第三行的構造函數裏面,把新節點的perv指向之前的last節點l,然後把之前last節點l的next指向新節點。最後一行modCount是集合中記錄修改次數的成員變量,和快速失敗機制有關。具體查看Java從入門到放棄(七)集合框架之ArrayList的坑

public void add(int index, E element) {
        checkPositionIndex(index);   //鏈表越界檢查

        if (index == size)
            linkLast(element);       //添加到last節點方法,如上 
        else
            linkBefore(element, node(index));  在node(index)節點前面添加新節點
    }

node(index)方法就是獲取鏈表中index位置的node:

Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {       //如果在鏈表的前半部分,就從first向後遍歷
            Node<E> x = first;
            for (int i = 0; i < index; i++)   循環index次
                x = x.next;
            return x;
        } else {
            Node<E> x = last;            //如果在鏈表的後半部分,就從last向前遍歷
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

可以看到內部做了優化,並不是直接遍歷到index位置,而是進行了判斷,當index在鏈表的前半部分就從first向後遍歷,如果在後半部分就從last向前遍歷。

linkBefore方法

void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;      //獲取succ的perv節點
        final Node<E> newNode = new Node<>(pred, e, succ);   //構建新節點,新節點的next指向succ,新節點的perv指向succ的perv   
        succ.prev = newNode;                                 //succ的perv節點指向新節點
        if (pred == null)                       
            first = newNode;            //如果perv節點爲null,說明succ是first節點,所以把first指向新節點
        else
            pred.next = newNode;        //succ的perv節點的next節點指向新節點
        size++;
        modCount++;
    }

執行如下代碼後結果如圖:

list.add(2,6)

3、remove方法

public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {    遍歷節點
                if (x.item == null) {
                    unlink(x); 
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
就是遍歷鏈表,找到對應的節點,然後用unlink方法刪除,看一下unlink方法
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;      //node節點存儲的數據
        final Node<E> next = x.next;   //後一個節點
        final Node<E> prev = x.prev;   //前一個節點

        if (prev == null) {          
            first = next;             //如果x的perv節點爲null,說明x節點就是first,所以要把x的next節點設爲first節點
        } else {
            prev.next = next;         //x節點的perv節點的next節點設置爲x節點的next節點
            x.prev = null;            //x的perv節點置爲null,便於gc
        }

        if (next == null) {           
            last = prev;   			  //如果x的next節點爲null,說明x節點就是last節點,所以要把x的perv節點設爲last節點          
		} else {
            next.prev = prev;         //x節點的next節點的perv節點設置爲x節點的perv節點
            x.next = null;            //x的next節點置爲null,便於gc
        }

        x.item = null;                //x的item置爲null,便於gc
        size--;
        modCount++;                    
        return element;
    }
如下代碼執行後:
list.remove(3);

remove(index)方法:

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
方法都是調用了之前的一些方法,鏈表越界檢查,然後根據node(index)知道對應的節點,使用unlink方法刪除,這兩個方法上面都有說明。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章