源碼分析——LinkedList

前言

上一篇咱們分析了ArrayList這個用數組實現的List集合類,今天繼續來分析一個跟它比較類似的List集合類——LinkedList,不過LinkedList的底層實現是鏈表,它們內部的實現仍是有很大差別的。java

1. 概覽

類間關係圖:


public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable複製代碼

能夠看到對比起 ArrayList,LinkedList並無實現RandomAccess 接口,因此咱們能夠知道它並不支持隨機訪問。 接下來咱們看看LinkedList的具體實現node

每一個鏈表存儲了 first 和 last 指針分別指向頭節點和尾節點: 
數組

//總節點數
transient int size = 0;
 //頭節點
transient Node<E> first;
//尾節點
transient Node<E> last;
複製代碼

能夠發現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;
    }
}複製代碼

下面是LinkedList的結構圖:數據結構


2. 添加

添加新元素多線程

public void add(E e) {
    checkForComodification();
    lastReturned = null;
    if (next == null)
        linkLast(e);
    else
        linkBefore(e, next);
    nextIndex++;
    expectedModCount++;
}複製代碼
添加到尾部或者插入到指定位置

//尾插到尾節點並將新節點設爲尾節點
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++;
}

//將新節點插入到指定節點前面
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++;
}
複製代碼

3. 修改

public E set(int index, E element) {
    // 檢查index是否合法
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}複製代碼

4. 刪除

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;
}複製代碼

5. fail-fast機制

值得關注的是LinkedList在使用迭代器時也有 fail-fast 機制,在迭代的同時對LinkedList進行告終構上的操做就會觸發,拋出ConcurrentModificationException異常。具體源碼以下:

public void add(E e) {
    checkForComodification();
    lastReturned = null;
    if (next == null)
        linkLast(e);
    else
        linkBefore(e, next);
    nextIndex++;
    expectedModCount++;
}

public void set(E e) {
    if (lastReturned == null)
        throw new IllegalStateException();
    checkForComodification();
    lastReturned.item = e;
}

public void remove() {
    checkForComodification();
    if (lastReturned == null)
        throw new IllegalStateException();

    Node<E> lastNext = lastReturned.next;
    unlink(lastReturned);
    if (next == lastReturned)
        next = lastNext;
    else
        nextIndex--;
    lastReturned = null;
    expectedModCount++;
}

// fail-fast機制
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}複製代碼

6. 與 ArrayList 的比較

ArrayList的具體分析能夠看一下個人上一篇文章:juejin.im/post/5d29d6…dom

  • ArrayList 基於動態數組實現,LinkedList 基於雙向鏈表實現;
  • 都是線程不安全的集合;
  • ArrayList 支持隨機訪問,LinkedList 不支持;
  • LinkedList 在任意位置添加刪除元素比 ArrayList 更快。

7. 總結

到這裏算是我對Java集合類源碼分析系列的第四篇文章了,從HashMap、ConcurrentHashMap到ArrayList,其實對這些集合類的源碼分析,我大體分紅幾個步驟分析講解:
  1. 類的繼承和實現關係
  2. 內部的數據結構
  3. 一些比較重要的增刪改查方法
  4. 這個容器的優缺點,多線程環境下是否安全等
  5. 與類似容器的對比,適用場景等
總結起來也就是這些,剩下還有一些其它的容器我沒分析到,但只要按上面這個思路去閱讀源碼並思考分析,就可以對這個容器有更加深入的理解,而不是僅僅停留在知道如何使用的層次上。
相關文章
相關標籤/搜索