Java 集合之LinkedList

1.回顧

在上一篇介紹了Java 集合之ArrayList主要講解了ArrayList的一些方法和具體實現,ArrayList是基於數組來實現,當插入新元素時,其後面的元素的位置都須要移動,這顯而易見是個影響性能的操做,數據量一大,再頻繁的執行插入操做那...照例咱們先看集合的結構圖 node

2.結構

LinkedList和ArrayList 同屬於List接口的,看下詳細的結構圖實現類,那麼二者的方法應該大體相同 數組

AbstractSequentialList類是 AbstractList的一個子類,提供了一個基本的list接口實現,爲了順序訪問的數據存儲結構(鏈表)提供了最小化的實現。AbstractSequentiaList是在迭代器基礎上實現的get、set、add等方法。bash

Deque接口繼承Queue接口,兩端都容許插入和刪除元素,即雙向隊列。源碼分析

實現了Cloneable,能被克隆,實現了Serializable,支持序列化post

咱們查看下LinkedList類中的方法性能

順便附上AbstractSequentiaList抽象類方法

經過查看源碼發現AbstractSequentiaList是在迭代器基礎上實現的get、set、add等方法,而這個迭代是在子類去實現

public abstract ListIterator<E> listIterator(int index);
複製代碼

3.源碼分析

一.成員變量

transient int size = 0;
transient Node<E> first;
transient Node<E> last;
複製代碼
  • size 元素個數
  • first 指向頭節點
  • last 指向尾節點

Node是個內部類ui

private static class Node<E> {
        E item; //結點的值
        Node<E> next;   //結點的後向指針
        Node<E> prev;   //結點的前向指針

        //構造方法中已完成Node成員的賦值
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;    //結點的值賦值爲element
            this.next = next;       //後向指針賦值
            this.prev = prev;       //前向指針賦值
        }
    }

複製代碼

二.構造方法

public LinkedList() {
    }

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

空構造方法構造了一個空的List 其中size爲0 first和last都爲null ,沒有任何元素 LinkedList(Collection<? extends E> c)構造一個包含指定Collection中全部元素的列表 該方法先調用空構造器 而後addAll()把Collection中全部元素添加進去this

三.經常使用方法

addAll

public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
 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) {////for循環結束後,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是否越界 將collection轉化成數組 循環數組將數組裏面的元素建立爲節點 並按照順序連起來 修改當前節點個數sizespa

add

public boolean add(E e) {
        linkLast(e);
        return true;
    }
   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++;
    }    
複製代碼

add 方法直接調用了 linkLast 方法,而 linkLast 方法是不對外開放的。該方法作了三件事情,新增一個節點,改變其先後引用,將 size 和 modCount 自增 1。其中 modCount 是記錄對集合操做的次數。指針

remove

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
 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;
    }    
複製代碼

檢查下標是否越界,而後調用 unlink 釋放節點。 還有其餘方法這裏就不一一列舉了

四 總結

結合源碼咱們大體能夠知道LinkedList裏維持了一個鏈表 每一個鏈表單元是一個Node Node的prev指向前一個節點 next指向後一個節點 這樣全部節點都串了起來 如圖

有幾點須要注意的是

  • LinkedList的實現是基於雙向鏈表的,且頭結點中不存放數據
  • LinkedList是基於鏈表實現的,所以不存在容量不足的問題,因此這裏沒有擴容的方法 不一樣與數組實現的ArrayList
  • LinkedList是基於鏈表實現的,插入刪除效率高,查找效率低

若常常查找又不多插入和刪除 則推薦使用ArrayList 相反常常插入與刪除 不多查找 則推薦使用LinkedList 平常開發中,固然是ArrayList的使用頻率更高些。下一篇準備講解下Set系列...

相關文章
相關標籤/搜索