LinkedList是基於雙向鏈表數據結構來存儲數據的,如下是對LinkedList 的 屬性,構造器 ,add(E e),remove(index),get(Index),set(inde,e)進行源碼分析:java
屬性:node
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; //指向末節點對象
2構造器:數據結構
public LinkedList() { //構造空的LinkedList對象
}
public LinkedList(Collection<? extends E> c) { //構造對象,將集合元素添加到新集合中
this(); addAll(c); }
3:方法:add(E e)源碼分析
public boolean add(E e) { linkLast(e); return true; }
linkedLast(e) 源碼this
/** * Links e as last element. */ void linkLast(E e) { final Node<E> l = last; //將原來的最末節點對象暫存 l 引用 final Node<E> newNode = new Node<>(l, e, null); /構建新的Node對象 last = newNode; //將鏈表對象的last引用指向新增的節點元素 if (l == null) first = newNode; //若是不存在以前指向的節點,則first引用指向新建立的節點對象 else l.next = newNode; //存在前一個節點,以前最後節點對象的next指向新建的節點對象 size++; //結合的長度加1 modCount++; }
Node對象的構造器以下:對象
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { //參數爲 l:以前的最後一個節點, element:須要新增的元素, next null this.item = element; //要增長的元素 this.next = next; //新增節點的next指向爲null this.prev = prev; //新增節點的prev指向以前的節點 } }
remove方法:blog
public E remove(int index) { //刪除指定索引的元素 checkElementIndex(index); //檢查是否索引越界 return unlink(node(index)); }
node(index) 的源碼以下:
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; } }
這裏有點繁瑣,舉個具體的實例說明:好比須要刪除index=5;的節點對象,假設結合的長度爲20索引
則調用 node(5) 方法後返回的是什麼呢?假設Node(0) 爲起始位置 內存
此時:初始:x=Node(0),當i=0 x=Node(1) i=1 x=Node(2)…… 當i=5-1 x=Node(5) 此時就定位到了須要刪除的節點對象 即 Node(index)element
接下來調用: unlink(node(index)) 繼續以index=5爲例
E unlink(Node<E> x) { // assert x != null; final E element = x.item; //Node(5).data final Node<E> next = x.next; //next=Node(6) final Node<E> prev = x.prev; //prev=Node(4) if (prev == null) { first = next; } else { prev.next = next; //Node(4).next=Node(6) x.prev = null; //Node(5).prev=null } if (next == null) { last = prev; } else { next.prev = prev; // Node(6).prev=Node(4) x.next = null; //Node(5).next=null 回收 } x.item = null; //Node(5)=null size--; modCount++; return element; }
這樣就完成了 Node(index-1).next=Node(index+1) Node(index+1).prev=Node(index-1) Node(index).data=null Node(index).prev=null Node(index).next=null 完成了刪除動做 刪除相應的索引的節點
刪除第一個節點和刪除最後一個節點的原理相似;
Get(int index) 方法:
public E get(int index) { checkElementIndex(index); //檢查索引是否越界 return node(index).item; //node(index) 在刪除的方法中分析過,返回索引爲index的節點對象, 因此get方法 返回的是該索引節點的存儲數據對象
}
set(index,e) 方法:
public E set(int index, E element) { checkElementIndex(index); Node<E> x = node(index); //調用node(index)放回Node(index) E oldVal = x.item; x.item = element; //將 Node(index)的引用指向新的對象
return oldVal; }
到此LinkedList的源碼分析結束了:
mark:使用LinkedList 時,使用的是鏈表結構,當調用add()方法時,默認添加到最後一個,集合不須要擴充,減小內存消耗;
可是當LinkedList 進行指定索引的查詢,元素替換,刪除,須要對集合從first指向開始進行遍歷一遍才能進行,有相應的計算複雜度;使用時應當考慮到這一點