JDK1.7原始碼筆記之LinkedList類
阿新 • • 發佈:2019-02-09
1. class 簡介
LinkedList是Java集合中常用的容器之一,內部是一個雙向連結串列(不是迴圈的),實現了List和Deque介面,可以同時當作列表、佇列和棧來使用,它提供了一系列對連結串列元素進行增刪改查的方法,有很多功能差不多但名字不同的方法,有些針對空值拋異常而有些則返回特殊值,使用時最好將其上轉型到相應的介面以限制它的功能。
2. class內部原理及特點
- 不是執行緒安全的,沒有Synchronized關鍵字。
- 允許向其中新增null。
- 內部是由一個一個的內部類Node前後指標連線組成的鏈,LinkedList相當於一個Node的管理類(如下圖所示),有兩根指標first和last一直指向Node鏈的頭部和尾部,其優勢在於增加和刪除元素較快,但不適合查詢操作。
- 其迭代器是快速失敗的,在一個LinkedList的迭代器建立之後呼叫非迭代器中的方法對容器的內部結構進行修改都會丟擲ConcurrentModificationException。
3. class原始碼細節分析
對雙向連結串列的操作無非就是對幾根指標進行玩耍,LinkedList中方法有不少,但是功能相同的很多,只是針對空值或者沒有元素的情況做出的反應不同。針對不同的介面,應該使用名字相關的方法,比如將LinkedList當佇列來使用時應該使用帶有offer,poll,peek等名字的方法,當成棧來使用時應該使用帶有push,peek,pop等名字的方法,以增加可讀性。
LinkedList的所有增刪改查方法基本上都和底層這幾個方法有關:linkFirst, linkLast, linkBefore, unlinkFirst, unlinkLast, unlink, node 。
linkFirst和linkLast
/* 頭插法 */
private void linkFirst(E e) {
final Node<E> f = first;
//建立一個新結點,其前指標為空,後指標指向first所指結點(可能為null)
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;//first指標始終指向Node鏈的頭
if (f == null) //如果LinkedList剛建立,此時新建立的結點既然是頭結點也是尾結點
last = newNode;
else
f.prev = newNode;//如果頭插之前有結點,將其前指標指向新結點
size++;
modCount++;//記錄修改次數,迭代時檢測快速失敗
}
/* 尾插法 */
void linkLast(E e) {//操作和頭插相反
final Node<E> l = last;
//建立一個新結點,其後指標為空,前指標指向last所指結點(可能為null)
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;//last指標始終指向Node鏈的尾
if (l == null) //如果LinkedList剛建立,此時新建立的結點既然是尾結點也是頭結點
first = newNode;
else
l.next = newNode;//如果尾插之前有結點,將其後指標指向新結點
size++;
modCount++;
}
linkBefore
/* 在指定結點前插入 */
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
//建立一個新結點,其前指標指向指定結點的前驅,後指標指向指定結點的後繼
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;//指定結點的前指標連上新結點
if (pred == null)//LinkedList剛建立
first = newNode;
else
pred.next = newNode;//指定結點的前驅結點的後指標連上新結點
size++;
modCount++;
}
unlinkFirst, unlinkLast和unlink
/* 刪除頭結點 */
private E unlinkFirst(Node<E> f) {//這個f必須是頭結點
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // 標記一下有利於垃圾回收
first = next;//first指標始終指向頭結點
if (next == null)//LinkedList剛建立
last = null;
else
next.prev = null;//next此時為頭結點
size--;
modCount++;
return element;
}
/* 刪除尾結點 */
private E unlinkLast(Node<E> l) {//l必須是尾結點
final E element = l.item; //和上面一樣
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
/* 刪除某個結點 */
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) {//LinkedList剛建立時
first = next;
} else {
prev.next = next;//前驅連線上後繼
x.prev = null;//刪除
}
if (next == null) {//LinkedList剛建立時
last = prev;
} else {
next.prev = prev;//後繼連線上前驅
x.next = null;//刪除
}
x.item = null;
size--;
modCount++;
return element;
}
node
/* 查詢雙向連結串列從頭結點往尾結點數的第index個結點 */
Node<E> node(int index) {
if (index < (size >> 1)) {//如果index小於連結串列總長度的一半,則從頭往尾找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //如果index大於連結串列總長度的一半,則從尾往頭找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
4. 總結
LinkedList是常用的java集合之一。當要頻繁地進行插入和刪除操作時,使用LinkedList效果較好,儘量不要對其進行查詢的操作。LinkedList既可以當列表使用,還可以充當佇列和棧,使用時最好將其上轉型到相應的介面以限制它的功能。