LinkedList集合底層原理

Java集合的LinkedList底層詳解
前一篇文章,已經講過ArrayList的底層實現原理,今天學習LinkedList底層實現原理。


LinkedList類是List接口的實現類,它是一個集合,可以根據索引來隨機的訪問集合中的元素,還實現了Deque接口,它還是一個隊列,可以當成雙端隊列來使用。雖然LinkedList是一個List集合,但是它的實現方式和ArrayList是完全不同的,ArrayList的底層是通過一個動態的Object[]數組實現的,而LinkedList的底層是通過鏈表來實現的,因此它的隨機訪問速度是比較差的,但是它的刪除,插入操作很快。

 

  • LinkedList是基於雙向循環鏈表實現的,除了可以當作鏈表操作外,它還可以當作棧、隊列和雙端隊列來使用。
  • LinkedList同樣是非線程安全的,只在單線程下適合使用。
  • LinkedList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸。


基本屬性:

[Java] 純文本查看 複製代碼
1
2
3
transient int size = 0;     //LinkedList中存放的元素個數[/size]
[size=3]transient Node<E> first;    //頭節點
transient Node<E> last;     //尾節點



LinkedList數據結構原理
LinkedList底層的數據結構是基於雙向循環鏈表的,且頭結點中不存放數據,每個節點都有一個前驅和後繼,如下


部分源碼:
添加方法

[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class LinkedList<E>extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
 
    transient int size = 0;   //LinkedList中存放的元素個數
 
    transient Node<E> first;  //頭節點
     
    transient Node<E> last;   //尾節點
 
     //構造方法,創建一個空的列表
    public LinkedList() {
    }
 
    //將一個指定的集合添加到LinkedList中,先完成初始化,在調用添加操作
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
 
    //插入頭節點
    private void linkFirst(E e) {
        final Node<E> f = first;  //將頭節點賦值給f節點
        //new 一個新的節點,此節點的data = e , pre = null , next - > f
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode; //將新創建的節點地址複製給first
        if (f == null//f == null,表示此時LinkedList爲空
            last = newNode;  //將新創建的節點賦值給last
        else
            f.prev = newNode;  //否則f.前驅指向newNode
        size++;
        modCount++;
    }
 
    //插入尾節點
    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++;
    }
 }




添加方法默認是添加到LinkedList的尾部,首先將last指定的節點賦值給l節點,然後新建節點newNode,此節點的前驅指向l節點,data = e,next = null,並將新節點賦值給last節點,它成爲了最後一個節點,根據當前List是否爲空做出相應的操作。若不爲空將l的後繼指針修改爲newNode,並且size++,modCount++;

刪除方法:

[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/*
    LinkedList的刪除操作,從前往後遍歷刪除
*/
public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
}




返回指定位置的節點信息:

[Java] 純文本查看 複製代碼
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
//返回指定位置的節點信息
    //LinkedList無法隨機訪問,只能通過遍歷的方式找到相應的節點
    //爲了提高效率,當前位置首先和元素數量的中間位置開始判斷,小於中間位置,
    //從頭節點開始遍歷,大於中間位置從尾節點開始遍歷
    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;
        }
    }




List實現類的使用場景
ArrayList,底層採用數組實現,如果需要遍歷集合元素,應該使用隨機訪問的方式,對於LinkedList集合應該採用迭代器的方式
如果需要經常的插入。刪除操作可以考慮使用LinkedList集合
如果有多個線程需要同時訪問List集合中的元素,開發者可以考慮使用Collections將集合包裝成線程安全的集合(Collections.synchronizedList(new LinkedList<>()))。

總結:

  • LinkedList是一個功能很強大的類,可以被當作List集合,雙端隊列和棧來使用。
     
  • LinkedList使用鏈表保存集合中的元素,因此隨機訪問的性能較差,但是插入刪除時性能高
     
  • LinkedList基於鏈表實現的,因此不存在容量不足的問題,所以沒有擴容的方法
     
  • 注意源碼中的Node node(int index)方法。該方法返回雙向鏈表中指定位置處的節點,而鏈表中是沒有下標索引的,要指定位置出的元素,就要遍歷該鏈表,從源碼的實現中,我們看到這裏有一個加速動作。源碼中先將index與長度size的一半比較,如果index<size/2,就只從位置0往後遍歷到位置index處,而如果index>size/2,就只從位置size往前遍歷到位置index處。這樣可以減少一部分不必要的遍歷,從而提高一定的效率(實際上效率還是很低)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章