LinkedList詳解及源碼分析
1.LinkedList概述:
LinkedList相較於ArrayList而言,ArrayList底層是維護了一個數組,而LinkedList是維護了一個雙向鏈表。所以在增刪元素時,LinkedList效率要略高,而隨機訪問時ArrayList效率則會略高。
在LinkedList中,所有對內部元素的操作都是基於雙向鏈表的需要來進行的,這裏會判斷操作元素的位置是靠近開頭還是結尾,之後我會解釋Node()方法 。同樣的,LinkedList也是不同步的。
2.LinkedList繼承實現體系:
這裏由於LinkedList實現了Queue接口,提供更豐富的操作方法,比如:offer()、peek()、poll()
3.LinkedList主要屬性介紹:
//儲存元素的長度
transient int size = 0;
//第一個元素
transient Node<E> first;
//最後一個元素
transient Node<E> last;
這裏的Node是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;
}
}
Node類主要包括當前元素、前一個元素、後一個元素三個屬性,這就是雙向鏈表的體現形式
4.LinkedList的構造方法:
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
這裏我們主要說一下addALL()方法:
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跟後一個node
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);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//最後判斷插入的最後一個node是否爲鏈表中最後一個node
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
//長度增加
size += numNew;
//操作數增加1
modCount++;
return true;
}
這裏主要就是通過對插入位置的前一個node與後一個node的修改來完成的,這裏指的注意的是Node(index)方法,它的作用就是得到指定位置的node
Node<E> node(int 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;
}
}
size>>1=size/2,查找時,通過判斷查找索引與size/2的大小來決定是從頭開始查找還是從尾開始查找。通過解析這個方法,我們可以看出當查找元素時,爲什麼LinkedList效率要略低於ArrayList了。
5.LinkedList的操作方法:
add(E e): 將指定node添加到此鏈表的結尾。
public boolean add(E e) {
addBefore(e, header);
return true;
}
這裏調用了addBefore方法,它是LinkedList的私有方法。
private Entry<E> addBefore(E e, Entry<E> entry) {
//利用Entry構造函數構建一個新節點 newEntry,
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
//修改newEntry的前後節點的引用,確保其鏈表的引用關係是正確的
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
//容量+1
size++;
//修改次數+1
modCount++;
return newEntry;
}
add(E e)無非就是做了2件事:構建一個新節點newEntry,然後修改其前後的引用。
其他的操作元素的方法也是大同小異,並不複雜,大家可以自行閱讀,我就不一一例舉了。
對於LinkedList來說,最重要的就是理解它的數據結構是一個雙向鏈表,所有對該鏈表的操作都是對於裏面Node的修改前後引用來完成的,對於有數據結構基礎的同學來說,這應該不難理解。