1. 程式人生 > 實用技巧 >Java容器原始碼分析之LinkedList

Java容器原始碼分析之LinkedList

LinkedList簡介

LinkedList是一個使用雙向連結串列結構實現的容器,與ArrayList一樣,它能動態擴充其長度,LinkedList相較於ArrayList,其任意位置插入速度比ArrayList要快,但是其查詢速度要比ArrayList要慢;LinkedList繼承自AbstractSequentialList,實現了List、Deque、Cloneable、Serializable介面。

LinkedList UML圖如下:

和ArrayList一樣,LinkedList也不是一個執行緒安全的容器。

LinkedList原始碼分析

構造方法

LinkedList有兩個構造方法:

public LinkedList() {
}

//從已有的一個容器建立一個LinkedList物件
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

addAll()方法:

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}

public boolean addAll(int index, Collection<? extends E> c) {
    //檢查index是否溢位
    checkPositionIndex(index);
    
    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;
    //獲取第index位置的node元素和node的前一個元素
    //succ:第index位置的node元素
    //pred:index位置前一個node元素
    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;
}

add方法

LinkedList也有兩個add方法,如下:

public boolean add(E e) {
    //新增元素到隊尾
    linkLast(e);
    return true;
}

public void add(int index, E element) {
    //檢查index是否溢位
    checkPositionIndex(index);

    if (index == size)
        //index == size,直接新增到隊尾
        linkLast(element);
    else
        //index != size,新增元素到index位置
        linkBefore(element, node(index));
}

linkLast方法:

void linkLast(E e) {
    final Node<E> l = last;
    //新建一個node,將其前一個元素指標指向原連結串列的最後一個元素
    final Node<E> newNode = new Node<>(l, e, null);
    //更新尾指標
    last = newNode;
    if (l == null)
        //若原last==null說明此時連結串列就一個元素
        first = newNode;
    else
        //更新原連結串列尾元素指標
        l.next = newNode;
    size++;
    modCount++;
}

linkBefore方法:

void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    //獲取指定位node元素的前一個元素pred
    final Node<E> pred = succ.prev;
    //新建一個node,將其前指標指向pred元素
    final Node<E> newNode = new Node<>(pred, e, succ);
    //將指定位置的node元素的前指標指向新元素,完成插入
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

獲取指定位置node指標方法node:

Node<E> node(int index) {
    // assert isElementIndex(index);
    //index > size/2時,說明在連結串列前半段,從前往後搜尋
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    //index < size/2時,從後往前搜尋
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

get方法

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

get方法也比較簡單,首先檢測index是否溢位,然後直接找到index位置的元素,並返回其item。