LinkedList源碼詳解(1)

LinkedList源碼詳解

定義:LinkedList是一個實現了Deque和List接口的雙向鏈表集合,同時繼承了AbstractSequentialList。可以存儲任意元素包括null。

底層數據結構

特點:

  1. LinkedList是非線程安全的,可以使用Collections.synchronizedList獲取線程安全的LinkedList集合。
  2. LinkedList在隨機插入元素的時候效率比較高,因爲只需要修改節點的指向就能完成元素的插入。特別是元素離中間越遠,插入的速度越快。
  3. LinkedList的元素也是按順序存儲的。

結構圖

源代碼分析

  1. 前置條件(在LinkedList底層中,元素的存儲都基於自定義的節點對象,此對象可以存儲元素,以及可以指定它的上一個和想一個節點對象。)
      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. 定義的變量
  //集合的元素個數
  transient int size = 0;
  
  //預先定義的頭節點
  transient Node<E> first;
  
  //預先定義的末尾節點
  transient Node<E> last;

添加元素操作

  1. add(E e)方法 向集合的末尾增加一個元素 (LinkedList中add(E e)和addLast(E e)的效果一樣,返回值不同add方法是Collection類中指定的)
    // 調用具體操作方法linkLast(e);
    // 執行成功就返回true 表示增加元素成功
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    // 在鏈表末尾添加一個元素
    /**
     *  1.首先將當前末尾節點賦值給一個臨時節點對象
     *  2.創建一個新節點,將要插入的元素和上一個節點對象插入到改節點對象
     *  3.然後將最後一個節點賦值爲新創建的節點
     *  4.最後判斷起初的末尾節點是否是空,如果是空就代表集合中沒有節點,那麼新創建的節點對象就是初始節點,不爲空那麼將原本的末尾節點的下一個節點指向新創建的節點。
     *  5. 將集合元素個數+1,操作計數+1
     */
    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++;
    }
  1. addLast(E e) 向集合的末尾增加一個元素(實現過程和add方法一樣,只是它是隸屬於Deque的特定方法並且返回值爲void)
    public void addLast(E e) {
        linkLast(e);
    }
  1. addFirst(E e) 在集合的頭部添加指定的元素,此方法也隸屬於Deque的特定方法並且返回值爲void
    // 調用具體操作方法linkFirst(e);
    public void addFirst(E e) {
        linkFirst(e);
    }
    
    //實現過程和linkLast類似,只是是對頭節點的操作
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

4.add(int index, E element) 在指定位置插入指定元素

    //判斷index時候合法
    //判斷如果index等於size就相當於在末尾加入新的節點元素
    //否則調用linkBefore(element, node(index));進行操作
    //
    public void add(int index, E element) {
        checkPositionIndex(index);
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
    
    //判斷index在集合的前一半位置還是後一半位置,前一半就從第一個節點開始找不然從最後一個節點開始找,找到元素就返回。
    Node<E> node(int index) {
        // assert isElementIndex(index);
    
        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;
        }
    }

    //在指定節點前添加元素
    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;
        //如果之前的pred是null,那麼新節點就是頭節點,不然新節點的上一個節點是pred
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

4.addAll(int index, Collection<? extends E> c) 將目標集合中的所有元素添加到當前集合的指定位置後面

   // 1.判斷index是否合法
   // 2.將目標集合轉換爲數組並計算它的長度如果是0直接返回false
   // 3.定義兩個臨時節點用來記錄將要插入的節點的上一個節點pred後下一個節點succ:如果index正好是集合的size那麼,succ就是空的,pred就是末尾節點否則succ是定位到的節點位置,pred是定位到的節點位置的上一個節點
   // 4.遍歷目標數組創建新的節點,如果pred是空的話那麼就代表在頭節點插入新的節點,否則新節點就是pred的下一個節點。最後將新節點賦值爲pred。
   // 5. 當遍歷結束,判斷succ是否爲空如果爲空就代表pred就是最後一個元素,將它賦值給last Node,不然就將pred的下一個節點指向succ,將succ的上一個節點指向pred。
   // 6. size+1,modCount+1;返回true
   public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;
        if (index == size) {
            succ = null;
            pred = last;
        } else {
            succ = node(index);
            pred = succ.prev;
        }

        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }

        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }

        size += numNew;
        modCount++;
        return true;
    }


    // 判斷index是否合法
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }


  1. 將目標集合的元素添加到集合的末尾處
    // 這個方法是addAll(int index, Collection<? extends E> c) 的特殊情況,即index==size
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }

刪除元素操作

  1. removeFirst 刪除第一個元素
    // 1 獲取firstNode的Obj
    // 2 獲取 firstNode的下一個Node,判斷是否爲空,不爲空設置爲firstNode並且上一個Node設置爲null,最後將原本的firstNode設置爲null,first的Next也設置爲nul
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
  1. removeLast() 刪除最後一個元素
    // 判斷最後一個元素是否爲空 空拋出NoSuchElementException
    // 調用unlinkLast(l)
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }
    
    //獲取節點的元素,上一個節點
    //將元素設置爲null,上一個節點也設置爲null
    //將上一個節點設置爲last,如果上一個節點是空的話,將first設置爲空(表示集合中沒有元素)
    //不爲空則將prev的下一個設置爲null
    private E unlinkLast(Node<E> l) {
        // assert l == last && l != null;
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

3.remove(Object o) 移除指定的元素

    //判斷o是否爲空,遍歷容器 null用==進行判斷
    //調用unlink
    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;
    }
    
    //刪除元素
    // 拿到指定節點的上下節點
    // 判斷上下節點是否爲空:上節點爲空,等於刪除頭節點;下節點爲空等於刪除尾節點。
    // 否則上節點不爲空,prev的下一個節點指向next;下節點不爲空,next的上一個節點指向prev;
    // 當前節點的上下節點設置爲null;item也設置爲null
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }


4 remove(int index) 刪除指定位置的值

     //1判斷index是否合法
     //2獲取index位置的node
     //3刪除元素
     public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

  1. clear() 清空容器
    // 遍歷容器,將所有的節點都設置爲null
    public void clear() {
        // Clearing all of the links between nodes is "unnecessary", but:
        // - helps a generational GC if the discarded nodes inhabit
        //   more than one generation
        // - is sure to free memory even if there is a reachable Iterator
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }

  1. remove() 刪除第一個元素和removeFirst效果一樣。
    public E remove() {
        return removeFirst();
    }

修改操作

  1. set(int index, E element) 修改指定位置修改元素
    //1 判斷index是否合法
    //2 獲取指定位置的節點
    //3 獲取節點值
    //4 將新的值添賦予item
    //5 返回舊的值
    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

查詢操作

1.getFirst() 獲取第一個元素

    // 判斷first時候爲空,空拋出NoSuchElementException不然返回item
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

2.getLast() 獲取最後一個元素 和getFirst同理

 public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

3.get(int index) 獲取指定位置的元素

    //判斷index時候合法
    //調用node(index)獲取節點返回item
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

具體使用Demo

        public static void main(String[] args) {
        LinkedList<String> linkedListDemo = Lists.newLinkedList();

        System.out.println("===========add===========");
        linkedListDemo.add("element1");
        linkedListDemo.addLast("element2");

        List<String> list = Lists.newArrayList();

        list.add("element3");
        list.add("element4");
        List<String> list2 = Lists.newLinkedList();
        list2.add("element5");
        list2.add("element6");

        linkedListDemo.addAll(list);
        linkedListDemo.addAll(2, list2);
        linkedListDemo.add(3, "element7");
        System.out.println(linkedListDemo);

        System.out.println("===========delete===========");
        //delete
        linkedListDemo.remove("element6");
        linkedListDemo.remove();
        linkedListDemo.remove(0);
        linkedListDemo.removeFirst();
        linkedListDemo.removeLast();
        System.out.println(linkedListDemo);


        System.out.println("===========update===========");
        //update
        linkedListDemo.set(1, "element8");
        System.out.println(linkedListDemo);

        //get
        System.out.println("===========get===========");
        String index = linkedListDemo.get(0);
        System.out.println(index);
        String first = linkedListDemo.getFirst();
        System.out.println(first);
        String last = linkedListDemo.getLast();
        System.out.println(last);
    }

    結果:
    ===========add===========
    [element1, element2, element5, element7, element6, element3, element4]
    ===========delete===========
    [element7, element3]
    ===========update===========
    [element7, element8]
    ===========get===========
    element7
    element7
    element8
  • 總結:Linked的存儲基於一個雙向鏈表,因此對於隨機增加或者刪除元素塊(特別是添加或者刪除鏈表兩邊的元素);但是對於隨機訪問元素的速度就比較慢,因爲每次都需要遍歷鏈表查找元素。同時LinkedList是線程不安全的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章