LinkedList源碼解析

1.說明

LinkedList是對鏈表的擴展,其底層使用鏈表實現,不是線程安全的集合類。其繼承AbstractSequentialList,實現了List, Deque, Cloneable,Serializable各個接口,其中AbstractSequentialList繼承了AbstractList抽象類,AbstractList是對支持隨機讀取的List的部分功能的抽象,AbstractSequentialList是對不支持隨機讀取的List的一部分功能的抽象。而且LinkedList實現了Deque接口,表明其支持雙端隊列的全部功能。node

2.優缺點

LinkedList首先是鏈表的擴展,而且實現了雙端隊列的接口,因此其特色以下:安全

  • 隨機存儲,優勢是不須要提早申請空間
  • 具備雙端隊列的全部功能,能夠簡單地實現先進先出和後進先出
  • 新增和刪除時間複雜度比較少

3.重要變量

//元素的個數
transient int size = 0;

//首節點
transient Node<E> first;

//尾節點
transient Node<E> last;

4.節點數據結構

在此以前,先介紹一下雙端鏈表的數據結構數據結構

private static class Node<E> {
        //節點的值
        E item;
        //下一個節點,若是爲尾節點,則此值爲null
        Node<E> next;
        //前一個節點,若是爲首節點,則此值爲null
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

5.重要方法

//向鏈表頭添加元素
private void linkFirst(E e) {
        final Node<E> f = first;
        //設置新節點,令首節點等於新節點,並將新節點的next指向原來的首節點
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        //若是原來首節點爲空,表明爲空鏈表,則令尾節點也爲新節點
          ,不然,令原來首節點的前一個節點爲新節點
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        //元素個數與修改次數進行增長
        size++;
        modCount++;
    }
    
//向鏈表尾添加元素
void linkLast(E e) {
        final Node<E> l = last;
        //設置新節點,令尾節點指向新節點,而且將新節點的prev指向原來的尾節點
        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) {
        //獲取給定節點的前一個節點,設爲pred
        final Node<E> pred = succ.prev;
        //設置新節點,令其prev爲給定節點的前一個節點,next爲給定節點
        final Node<E> newNode = new Node<>(pred, e, succ);
        //同時設置給定節點的前一個節點爲新節點
        succ.prev = newNode;
        //判斷pred是否爲空,若是是,那麼新節點就是首節點
          ,不然,直接令pred的下一個節點爲新節點
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

//刪除首節點
private E unlinkFirst(Node<E> f) {
        final E element = f.item;
        final Node<E> next = f.next;
        //將首節點中相關引用置空,防止內存泄漏
        f.item = null;
        f.next = null;
        //令首節點爲首節點的下一個節點
        first = next;
        //若是首節點爲空,那麼直接將尾節點也置空,
          不然,將當前首節點的prev置爲null
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
//刪除尾節點
private E unlinkLast(Node<E> l) {
        final E element = l.item;
        final Node<E> prev = l.prev;
        //將尾節點中相關引用置空,防止內存泄漏
        l.item = null;
        l.prev = null; 
        //令尾節點爲尾節點的前一個節點
        last = prev;
        //若是尾節點爲空,那麼直接將首節點也置空,
          不然,將當前尾節點的next置爲null
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }
    
//刪除指定元素
E unlink(Node<E> x) {
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        
        //若是指定節點的前一個節點爲空,表明制定節點爲首節點
          ,令首節點爲指定節點的下一個節點,不然,指定節點
          的prev的next節點爲其自身的next節點,而且令指定節點
          的prev節點爲null
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        
        //同理,若是指定節點的下一個節點爲null,表明指定節點爲尾節點,令
          尾節點等於指定節點的上一個節點,不然,指定節點的next節點的prev節點
          等於其自身的prev節點,且令自身的next節點等於null
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        //最後,將指定節點的item也置爲null,防止內存泄漏
        x.item = null;
        size--;
        modCount++;
        return element;
    }
//按照索引值與對應集合類進行批量添加
public boolean addAll(int index, Collection<? extends E> c) {
        //檢驗index是否合法,就是檢查其是否在[0,size]範圍內,若是index爲size,就是尾節點
        checkPositionIndex(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        //若是集合類長度爲0,那麼直接返回失敗
        if (numNew == 0)
            return false;
        
        //若是index與size大小相等,直接設置插入位置爲尾節點
          ,不然,進行遍歷查找節點,設置查找到的節點的前一個節點爲插入節點
        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);
            //若是pred==null,表明index=0,此時直接插入首節點,令
              首節點等於新節點,不然,直接令pred的下一個節點爲新節點,而後依序插入
            if (pred == null)
                first = newNode;
            else
                pred.next = newNode;
            pred = newNode;
        }
        //若是,succ==null,表明index==size,則是直接從
          尾部進行插入,因此,直接令尾節點指向插入的最後一個節點便可
          ,不然,令最後一個插入節點的next等於succ,
          令succ的prev等於最後一個插入節點便可(其實就是進行了一個銜接)
        if (succ == null) {
            last = pred;
        } else {
            pred.next = succ;
            succ.prev = pred;
        }
        //對元素數量進行增長
        size += numNew;
        modCount++;
        return true;
    }
//根據索引查找對應的節點(調用這個方法的方法已經判斷過index,因此直接使用)
Node<E> node(int index) {
        //若是index靠近左邊,從first開始遍歷,不然,從last開始遍歷
        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;
        }
    }
相關文章
相關標籤/搜索