1. 程式人生 > >LinkedList源碼分析

LinkedList源碼分析

thead 新元素 數組類 增刪改查操作 行修改 -- list() 返回 組成

目錄

  • 目錄
  • 1. 概述
  • 2. 構造函數
    • 2-1. 無參構造函數
    • 2-2. 傳入集合的構造函數
  • 3. 存儲結構
  • 4. 操作
    • 4-1. 查詢
    • 4-2. 添加
    • 4-3. 刪除
    • 4-4. 修改
  • 5. 總結

目錄

1. 概述

  1. 通過類名可以想象到, 該類的結構是一個鏈表結構.
  2. 但是它是一個類似於數組的鏈表, 意思就是普通的添加操作與數組類似, 將新元素添加到鏈表的尾端. 並支持通過下標來進行訪問.
  3. 它實現了Deque接口, 提供了棧和隊列的操作, 也就是該類的主要功能吧.
  4. 對於元素的增刪改比ArrayList強; 對於元素的查詢不如ArrayList.

2. 構造函數

它提供了兩個構造函數, 比ArrayList少一個構造方法那就是沒有提供帶有初始化容量的構造方法, 也是與該類的存儲結構有關.

因為是鏈表結構, 所以不會有擴容操作, ArrayList的初始化容量是為了避免不必要的擴容操作.

2-1. 無參構造函數

/**
 * Constructs an empty list.
 */
public LinkedList() {
}

2-2. 傳入集合的構造函數

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

直接調用了addAll方法, 了解到這, 在下面解釋addAll方法.

3. 存儲結構

通過上面的解釋, 知道它是一個鏈表結構, 由節點(Node)組成, 每個節點之間有相互指向的引用.

4. 操作

操作包含三部分:

  1. List的增刪改查操作
  2. 棧的進棧出棧
  3. 隊列的進隊列出隊列

4-1. 查詢

方法名 備註
getFirst() 獲取頭節點, 不刪除
getLast() 獲取尾節點, 不刪除
peek() 獲取頭結點, 不刪除
peekFirst() 獲取頭結點, 不刪除
peekLast() 獲取頭結點, 不刪除
element() 調用getFirst()
poll() 調用unlinkFirst(E), 詳見4-3
pollFirst() 調用unlinkFirst(E), 詳見4-3
pollLast() 調用unlinkLast(E), 詳見4-3
pop() 調用 removeFirst(), 詳見4-3
node(index) 通過下標進行取值

對於Java來說, 註重業務實現, 所以函數以及變量的見名知意比較重要. 所以我們日常中使用的話, 偏getFirst()和getLast()的使用比較多.

對於node(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;
    }
}

下面我們看一下getFirst()和getLast()的源碼, 其它方法的源碼類似, 就是找到節點返回節點值.

/**
 * Returns the first element in this list.
 *
 * @return the first element in this list
 * @throws NoSuchElementException if this list is empty
 */
public E getFirst() {
    // 獲取頭結點
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    // 返回頭結點的值
    return f.item;
}

/**
 * Returns the last element in this list.
 *
 * @return the last element in this list
 * @throws NoSuchElementException if this list is empty
 */
public E getLast() {
    // 獲取尾節點
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    // 獲取尾節點的值
    return l.item;
}

4-2. 添加

添加的方法有:

方法名 備註
linkFirst(E) 添加一個元素到鏈表的頭部
linkLast(E) 添加一個元素到鏈表的尾部
addFirst(E) 調用linkFirst(E)
addLast(E) 調用linkLast(E)
add(E) 調用linkLast(E)
offer(E) 調用add(E)
offerFirst(E) 調用addFirst(E)
offerLast(E) 調用addLast(E)
push(E) 調用addFirst(E)

可以發現, 最終的方法落實到了linkFirst(E)和linkLast(E)兩個方法.

源碼:

/**
 * Links e as first element.
 */
private void linkFirst(E e) {
    // 獲取鏈表的頭節點
    final Node<E> f = first;
    // 創建一個新節點
    final Node<E> newNode = new Node<>(null, e, f);
    // 使頭結點為新節點
    first = newNode;
    
    if (f == null)
        // 如果原先的頭結點為null, 說明鏈表為空鏈表, 給尾節點也為該新節點
        last = newNode;
        // 否則頭結點的prev指向新節點
    else
        f.prev = newNode;
        
    // 改變元素數量的大小
    size++;
    // 鏈表結構的改變次數
    modCount++;
}

/**
 * Links e as last element.
 */
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++;
}

4-3. 刪除

方法名 備註
unlink(E) 刪除一個元素
unlinkFirst(E) 刪除頭結點
unlinkLast(E) 刪除尾節點
remove(index) 刪除下標元素
remove() 調用removeFirst()
removeFirst() 調用unlinkFirst(E)
removeLast() 調用unlinkLast(E)

刪除操作就會涉及到節點之間引用關系的改變.

比如:

A <-> B <-> C  => A <-> C

先把B的prev的next指向B的next, 再把B的next的prev指向B的prev, 然後把B置為null.

至於其它的刪除方法, 也與之類似, 改變節點引用, 該節點置為null, size--, modCount--.

4-4. 修改

修改操作幾乎用不到, 也是使用了List接口的set(int, E)方法.

很簡單, 找到具體的元素進行修改即可.

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}

5. 總結

  1. 基於鏈表結構的存儲方式, 隨機訪問性能差, 元素的增刪性能比較好.
  2. 沒有擴容操作, 同等元素的情況下, 占用內存比ArrayList多, 因為還要存儲節點之間的引用.
  3. 可以作為棧或者隊列使用.

不要因為知識簡單就忽略, 不積跬步無以至千裏.

LinkedList源碼分析