jdk原始碼閱讀——linkedlist
阿新 • • 發佈:2018-11-10
首先還是從建構函式開始
/**
* Constructs an empty list.
*/
public LinkedList() {
}
是一個空的
然後我們從add看
public boolean add(E e) {
linkLast(e);
return true;
}
定位到linkLast
void linkLast(E e) {
//這裡last 和 first 的定義是
//transient Node<E> last;
//transient Node<E> first;
final Node<E> l = last;
//構建一個新的點,上一個節點執行當前連結串列的last,下一個節點設定為null
final Node<E> newNode = new Node<>(l, e, null);
//將表示當前連結串列最後一個節點的last指標指向新的節點
last = newNode;
//判斷新建的節點是不是整個連結串列的第一個節點
if (l == null)
//是的話 first指標指向新節點
first = newNode;
else
//不是的話 讓以前的最後一個節點的下一個節點指標,指向最新、最後的節點
l.next = newNode;
//改變連結串列裡資料的個數
size++;
//增加操作次數,迭代的時候用的著
modCount++;
}
//觀察發現這個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;
}
}
現在看一下迭代器部分
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
//拿到當前操作次數,相當於版本號
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
//檢視有沒有下一個節點
public boolean hasNext() {
//看下一個節點,是否大於總的長度
return nextIndex < size;
}
public E next() {
//判斷迭代的連結串列有沒有被修改
checkForComodification();
//如果已經沒有下一個節點了丟擲異常
if (!hasNext())
throw new NoSuchElementException();
//這裡可以看出next是指向下一個節點的。lastReturned是最後一個返回的,也就是這次迭代要返回給呼叫者的
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
//在看一下比較重要的刪除
public void remove() {
//先檢查連結串列有沒有改變
checkForComodification();
//remove要在next方法後呼叫,所以這裡的lastReturned表示正在訪問的節點
//如果沒有就不能移除
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
//unlink的程式碼就不貼了,就是很簡單的雙向連結串列刪除節點
//只要把前後兩節點連線在一起就行了
unlink(lastReturned);
//這裡在移動一下 其他指標,具體為什麼畫一下圖就明白說說反而說不清
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
//將刪除的點null 垃圾回收
lastReturned = null;
expectedModCount++;
}
}
剛才迭代器的建構函式中有一個node方法我們看下
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;
}
}
現在看一下remove方法
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;
}
可以看出 ,首先是判斷了一下是不是刪除一個null節點,
然後從頭開始找,這裡的刪除的話也沒有利用到雙向連結串列的優點
當然還有一個方法是這樣的
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
根據節點的編號,那就能利用到雙向連結串列的優點很快的找到刪除
之後get和set方法 也是一樣很簡單
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
checkElementIndex 的作用是判斷你要要找的點的index是不是大於0小於size
之後的話就是通過node函式去找,上面有node函式的介紹
總結一下
linkedlist 底層是一個雙向連結串列,查點的時候會先判斷點在前半段還是後半段,他增加刪除節點的時候能充分利用連結串列的特性不需要移動元素,但是查詢的時候需要遍歷,沒有arraylist快