java集合之LinkedList詳解
我們上一次說到List的ArrayList,我們這次去看下LinkedList---顧名思義是連結串列,連結串列的優點就不用說了吧,增刪效率比較高(具體的朋友們上網看吧),先來看下LinkedList的整體構架:
首先我們看到了LinkedList間接的實現了List介面(說明LinkedList是有list的特性的,add,remove等)、實現了Cloneable(可複製)、Serializable(進行了序列化),除此之外還有一個東西還實現了Queue(佇列,說明應該是有佇列的一些特性,pop等),這次先不側重queue的東西,我們主要看LinkedList是如何實現連結串列的。
//連結串列的長度 transient int size = 0; /** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ //連結串列的頭 transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ //連結串列的尾 transient Node<E> last; //連結串列維護的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; } }
我們可以看到,我們連結串列維護了一個size、first(頭)和last(尾),我們可以看到Node的結構中有個item、next、prev充分說明了此連結串列是一個雙向連結串列。
public static void main(String[] args){ //初始化linkedList List<String> linkedList = new LinkedList<>(); //新增元素 linkedList.add("abc"); linkedList.add("bcd"); return; }
這個空的建構函式沒有什麼內容,所以我們就跳過去,我們直接看add方法具體做的什麼。
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
//看方法註釋,我們可以看到是將該元素新增到此連結串列的end,返回true
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
//首先取出last節點
final Node<E> l = last;
//將newNode的prev指向last,後節點指向null,因為newNode永遠都是作為last的,所以next指向null
final Node<E> newNode = new Node<>(l, e, null);
//last指向newNode
last = newNode;
//如果連結串列為null的話則first也指向newNode,此時連結串列只有一個節點first和last都指向一個
//如果連結串列部位null,只需要將last的next指向這個newNode就行了,這樣就形成了雙向連結串列
if (l == null)
first = newNode;
else
l.next = newNode;
//最後size++
size++;
modCount++;
}
LinkedList就是這樣一個節點一個節點關聯起來的,我們可以看一下下面的圖片(這裡偷個懶,網上隨便找了一張):
我們接下來看一下remove(int index)刪除指定位置物件的方法,其中個人覺得有個有趣的地方,所以給大家分享下:
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
//檢查index是否超出範圍size
checkElementIndex(index);
//刪除指定位置的節點,首先得找到這個節點
return unlink(node(index));
}
//檢查index是否正確
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Returns the (non-null) Node at the specified element index.
*/
//返回指定index位置的節點
Node<E> node(int index) {
// assert isElementIndex(index);
//首先去比較index和size >> 1(也就是size的一半),如果比中間數小則從連結串列頭找,否則從尾找
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;
}
}
我們可以看到,我們想要移除指定位置的節點,首先得找到這個節點(這個是重點),關鍵我們的連結串列如何找到指定位置?不知道大家有沒有自己比較好的方法,我們來看下jdk的思路(我覺得jdk的實現方法很有趣),node(int index)方法中的if條件拿index和size的一半比較,文字解釋太麻煩了,我來畫個圖吧:
假設:我們linkedList的長度為6,如要remove的index為3,此時我們3 < (size >> 1)為false,則我們從linkedList後面開始直到變數i>index,此時i變數位置的就是index位置的node,如果我們index是2,那麼是從前面開始的,我們大家想這樣其實是把linkedList從中間分為2個,速度是不是比沒分之前快兩倍呢,哈哈哈~
總結:linkedList為雙鏈表,維護的是一個first和last指標,而每個節點有item自身、prev和next兩個節點來維護雙鏈表的關係,其他的功能都是圍繞我們的雙鏈表來進行的,有興趣的大家可以仔細研究一下原始碼,有時會發現很有趣的小細節哦~