linkedlist的底層結構是線性表的雙向鏈表,每一個節點包括兩個指針域(一個指向前驅結點,一個指向後繼結點)和一個數據域,由於雙指針域的獨特結構,因此其擁有增刪快和存取慢的特色。鏈表結構不須要預分配存儲空間,增長新的結點再去內存中申請便可,不會形成內存浪費和碎片化。java
AbstractSequentialList是List接口的簡化版,支持按次序訪問,abstractList支持隨機訪問。node
list接口許多經常使用的基礎方法,如set,get,indexof,remove等數組
Deque是一個雙端隊列接口,提供了相似棧、隊列,push,pop,peek的方法安全
Cloneable表明能夠被複制數據結構
Serializable表明能夠被序列化函數
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
和Arraylist同樣,linkedlist也是一個非線程安全的集合,只能在單線程環境下使用。若想得到一個線程安全的linkedlist可使用:this
List<Object> list = Collections.synchronizedList(new LinkedList<>());
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
size:雙向鏈表中節點的個數
first:指向鏈表的首結點
last:指向鏈表的尾結點線程
public LinkedList() {} public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
構造函數只有兩個,第一個很是簡單,構造一個空的linkedList。第二個有參構造就是全部Collection下的子類都經過toArray變爲數組,而後經過遍歷生成節點插入雙向鏈表中。指針
在閱讀核心方法以前,咱們首先須要瞭解它的核心內部類Node和ListItr.code
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; } }
ListItr
AbstractSequentialList定義了抽象方法listIterator,linkedlist實現此方法,並返回迭代器ListItr;其結構爲:
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; }
其實迭代器的核心就是遊標,若是不清楚迭代器能夠自行查詢相關原理,lastReturned是遊標前的元素,迭代器中修改數據結構的方法操做的都是lastReturned,next就是遊標後元素,nextIndex爲next的索引,expectedModCount
爲父類AbstractList的modCount,由於是線程不安全的類,用來觸發fast-fail機制。用來遍歷iterator的有hasNext(),next(),hasPrevious(),previous()。經過iterator修改linkedlist的有remove(),set(),add(),操做的都是遊標前元素lastReturned,而且會根據modcount來觸發fast-fail機制的檢驗,這些方法的實現依賴於linkedlist的核心方法。 終於說完ListItr,強烈建議大家本身看下源碼,接下來讓咱們一塊兒看下linkedlist的實現:
//將頭結點賦值給final f,新建一個新的結點newNode,將newNode定義爲頭節點。若是f=null,說明此前是個空鏈表,因此last也定義newNode,不然將原頭節點f(現第二個節點)的前指針指向newNode private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; } //將尾結點指向final l,聲明一個新的結點newNode(頭指針指向尾結點,尾指針指向null),將newNode定義 爲新的尾結點。若是l=null,表明原鏈表沒有尾節點(空鏈表),則將newNode也設爲頭節點。不然將原尾節點l的尾指針指向newNode。 void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; } //在某結點以前插入元素。在AC結點中插入b元素,將b元素生成B結點,將B節點的前指針指向A,後指針指向C,將C的前指針指向B,將A的後指針指向B void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; } //將結點從鏈表中分離。 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) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
//根據索引隨機訪問linkedlist,爲get方法的真正實現。 //size右移1位,大體至關於size/2.若index>size/2,則從尾結點向前遍歷,不然從頭結點向後遍歷 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; } } public E set(int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; } public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } //查詢o最後一次出現的索引,將o分爲兩種狀況,一種爲==null,另外一種用equals比較,後序遍歷得索引值。 public int lastIndexOf(Object o) { int index = size; if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { index--; if (x.item == null) return index; } } else { for (Node<E> x = last; x != null; x = x.prev) { index--; if (o.equals(x.item)) return index; } } return -1; }
其實只要你對雙向鏈表結構比較熟悉,那linkedlist源碼讀起來就會很輕鬆。linkedlist不須要分配存儲空間,只要有就能夠分配,元素個數也不受限制,在找到指定索引的結點後,進行增刪改都是O(1)的操做,效率很是高。在遍歷linkedlist的時候,最好使用foreach或者iterator,嚴禁直接使用get()遍歷。