天天看點

【深入Java基礎】LinkedList源碼分析LinkedList源碼分析

LinkedList源碼分析

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;
        }
    }
           

全局變量

雖然這不是用指針實作的,但是為了表述還是用“指針”和“指向”來說明(也可以說為引用)。

transient int size = ;//連結清單元素個數

    transient Node<E> first;//頭指針

    transient Node<E> last;//尾指針
           

構造方法

有兩個構造方法。

public LinkedList() {
    }

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

添加頭節點

private void linkFirst(E e) {
        final Node<E> f = first;//f指向頭節點
        final Node<E> newNode = new Node<>(null, e, f);//建立新節點,元素為e,新節點的前驅節點為null,後繼節點為f
        first = newNode;//頭指針指向新節點
        if (f == null)
            last = newNode;//如果原頭節點為空(說明連結清單為空),則将尾指針指向新節點
        else
            f.prev = newNode;//否則原節點的前驅節點指向新節點
        size++;//元素數量加1
        modCount++;//連結清單結構改變次數加1
    }
           

添加尾節點

這裡并沒有用private修飾,而添加頭節點确用了private,不知道為什麼。

void linkLast(E e) {
        final Node<E> l = last;//l指向尾節點
        final Node<E> newNode = new Node<>(l, e, null);//建立新節點,元素為e,新節點的前驅節點為l,後繼節點為null
        last = newNode;//尾指針指向新節點
        if (l == null)
            first = newNode;//如果原尾節點為空(說明連結清單為空),則将頭指針指向新節點
        else
            l.next = newNode;//否則原尾節點的後繼節點指向新節點
        size++;//元素個數加1
        modCount++;//連結清單結構改變次數加1
    }
           

在非空節點前插入新節點

void linkBefore(E e, Node<E> succ) {
        // assert succ != null;//聲明succ不為空
        final Node<E> pred = succ.prev;//succ的前驅節點
        final Node<E> newNode = new Node<>(pred, e, succ);//新節點,前驅指向pred,後繼指向succ
        succ.prev = newNode;//succ的前驅指向新節點
        if (pred == null)
            first = newNode;//如果前驅為null,則目前插入的新節點為頭節點,頭指針指向它
        else
            pred.next = newNode;//否則pred的next指向新節點
        size++;
        modCount++;
    }
           

删除頭節點和尾節點

/**
     * 删除頭節點
     */
    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;
    }

    /**
     * 删除尾節點
     */
    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;
    }
           

删除非空節點

對于非空節點的删除的判斷較多。

E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;//目前元素
        final Node<E> next = x.next;//x的前驅節點
        final Node<E> prev = x.prev;//x的後繼節點

        if (prev == null) {
            first = next;//如果前驅節點為空,則目前節點為頭節點,删除目前節點x後,頭指針指向x的後繼
        } else {
            prev.next = next;//否則x的前驅節點的後繼指向x的後繼節點
            x.prev = null;//x的前驅指向null
        }

        if (next == null) {
            last = prev;//如果後繼節點為空,則目前節點為尾節點,删除目前節點x後,尾指針指向x的前驅節點
        } else {
            next.prev = prev;/否則x的後繼節點的前驅指向x的前驅節點
            x.next = null;//x的後繼指向null
        }

        x.item = null;//x的元素指派為null便于GC回收空間
        size--;
        modCount++;
        return element;
    }
           

LinkedList主要的連結清單操作就這麼多,對外公開的方法都是基于以上這些操作的,以下是源碼:

/**
     * 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;
    }

    /**
     * Removes and returns the first element from this list.
     *
     * @return the first element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

    /**
     * Removes and returns the last element from this list.
     *
     * @return the last element from this list
     * @throws NoSuchElementException if this list is empty
     */
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

    /**
     * Inserts the specified element at the beginning of this list.
     *
     * @param e the element to add
     */
    public void addFirst(E e) {
        linkFirst(e);
    }

    /**
     * Appends the specified element to the end of this list.
     *
     * <p>This method is equivalent to {@link #add}.
     *
     * @param e the element to add
     */
    public void addLast(E e) {
        linkLast(e);
    }
           

以上操作的時間複雜度都是O(1),是以LinkedList對頭/尾節點的插入删除操作效率還是比較高的。但是對于查找,以及修改等操作的就不高效了,時間複雜度變為O(n)

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

查找元素

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

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

        if (index < (size >> )) {
            Node<E> x = first;
            for (int i = ; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - ; i > index; i--)
                x = x.prev;
            return x;
        }
    }
           

添加元素

public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
           

修改元素

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

删除元素

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
           

對于LinkedList和ArrayList的效率總結如下:

**直接添加元素:**LinkedList和ArrayList都是在尾部添加的。對于ArrayList來說,因為自動增長機制,在數組複制上會花費大量時間,是以資料量大的情況下,LinkedList的添加效率更高。

指定位置插入/删除/修改元素:這種方式下,兩種方式都要先找到指定位置的元素,然後再進行插入操作,時間複雜度都為O(n)。但是ArrayList還需要數組複制,是以會相對慢一些。

随機通路:基于數組的ArrayList的随機通路時間遠小于LinkedList 的,因為LinkedList需要移動指針。

是以資料不是經常變動時,用ArrayList好,而需要平凡改動時,用LinkedList好。如果需要線程同步,則使用Vector,因為ArrayList和LinkedList都不是同步的

另外LinkedList經常被用作隊列及棧使用

實作隊列操作:

public E peek() {//檢視第一個,但不删除
        final Node<E> f = first;
        return (f == null) ? null : f.item;
    }

    public E element() {//檢視第一個,但不删除
        return getFirst();
    }

    public E poll() {//擷取第一個,并且删除
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    public E remove() {//擷取第一個并且删除
        return removeFirst();
    }

    public boolean offer(E e) {//在隊列尾部添加一個
        return add(e);
    }
           

實作雙向隊列操作:

K

實作棧的操作:

public E poll() {//檢視棧頂但不删除
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }

    public void push(E e) {//壓棧
        addFirst(e);
    }

    public E pop() {//出棧
        return removeFirst();
    }
           

兩篇參考文章:

  • https://www.cnblogs.com/yakovchang/p/java_linkedlist.html
  • https://www.cnblogs.com/springfor/p/3985333.html