Java原始碼--LinkedList原始碼概述
阿新 • • 發佈:2018-11-09
與ArrayList同為List,LinkedList卻展現出不同的特性。作為java.util下的另一重要容器,我們下面來探究一下LinkedList的原始碼實現及特性分析。
上篇文章講述到,ArrayList用陣列來儲存資料,伴隨資料量的變大,ArrayList動態擴充陣列容量。
與之不同,LinkedList使用連結串列來儲存資料,因此它在插入/刪除資料方面有著天然的優勢,而在讀取指定位置的元素時,效能卻不及ArrayList速度快。
LinkedList儲存元素的變數如下:
transient Node<E> first;
儲存資料的節點:Node類如下:
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; //後節點
}
}
可以看出來,所有元素節點連在一起,構成了雙向連結串列(prev指向前一個元素節點,next指向後一個元素節點)。所有資料,儲存在Node的item中。相比於單向連結串列,雙向連結串列提供了兩種操作的方向,因此可以提供更快的遍歷查詢速度,更快的插入、刪除元素速度
下面看一下LinkedList內部對於雙向連結串列的維護:
在此之前,需要知道的一點:LinkedList額外儲存了first和last兩個節點,分別指向首位和末尾,作為雙向連結串列的兩端入口。
內部維護連結串列的一系列函式如下(private、protected):
//用e構造Node,將資料插到首位 private void linkFirst(E e) { final Node<E> f = first; //取得首位節點node final Node<E> newNode = new Node<>(null, e, f); //構造節點,item為e,prev為null,next指向當前的首位f first = newNode; //將first指向newNode,以它作為新的首位 if (f == null) last = newNode; //若之前的首位f不存在,則新的首位節點newNode在作為first的同時,也是last else f.prev = newNode; //若之前的首位f存在,則將f的prev指向新的首位節點newNode size++; modCount++; } //用e構造Node,將資料插到末尾,原理類似 void linkLast(E e); //將節點插入到指定節點前 void linkBefore(E e, Node<E> succ) { final Node<E> pred = succ.prev; //取得原先succ之前的節點pred,現在需要將newNode插在succ之前,pred之後 final Node<E> newNode = new Node<>(pred, e, succ); //構造內容為e的newNode,prev指向pred,next指向succ succ.prev = newNode; //將succ的prev指向newNode if (pred == null) first = newNode; //若pred為空,則將newNode置為first else pred.next = newNode; //將pred的next指向newNode size++; modCount++; } //將首位first節點從連結串列去掉 private E unlinkFirst(Node<E> f); //將末位last節點從連結串列去掉 private E unlinkLast(Node<E> l); //將任意位置的節點x從連結串列去掉 E unlink(Node<E> x) { //分別考慮prev與next為null和非null的情況,修復prev與next的指向 }
基於上述連結串列操作函式,LinkedList開放了如下介面(public)
public E getFirst() { //取得first內部的item,返回 } public E getLast() { //取得last內部的item返回 } public E removeFirst() { //呼叫unlinkFirst(Node<E> f), 將first從連結串列移除 } public E removeLast() { //呼叫unlinkLast(Node<E> l), 將last從連結串列移除 } public void addFirst(E e) { //將e插入到first之前 } public void addLast(E e) { //將e插入到last之後 } public boolean add(E e) { //呼叫linkLast(e),將e插入到last之後 } public boolean remove(Object o) { //從first開始遍歷連結串列,找到o,移除節點 } public int indexOf(Object o) { //從first開始遍歷節點,找到o,返回index } public int lastIndexOf(Object o) { //從last開始反向遍歷連結串列,找到o,移除連結串列 } public boolean contains(Object o) { //從first開始遍歷連結串列,找到o,返回true;或者找不到o,返回false } public boolean addAll(Collection<? extends E> c) { //1. 將集合c轉成陣列 //2. 遍歷陣列,對於每個元素,構造node,鏈在last後面 }
總結:LinkedList用雙向連結串列維護元素,相比於ArrayList提供了快速的插入、移除資料操作的同時,也比單向連結串列的遍歷查詢速度更快。但是,相比於ArrayList,LinkedList的查詢指定index元素效率低(ArrayList使用陣列儲存資料,可以直接依據索引讀取)。