LinkedList是List接口的第二個具體的實現類,第一個是ArrayList,前面一篇文章已經總結過了,下面咱們來結合源碼,學習LinkedList。java
基於雙向鏈表實現node
便於插入和刪除,不便於遍歷數組
非線程安全安全
有序(鏈表維護順序)數據結構
...源碼分析
上面是LinkedList的一些特性。學習
源碼以下所示:this
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
初步分析:線程
思考:code
咳咳,先谷歌一下,發現Deque的意思是雙端隊列,這裏已經能夠看出LinkedList是基於雙向鏈表的一些端倪了,帶着這點疑問,咱們繼續往下看。
源碼以下所示:
transient int size = 0; transient Node<E> first; transient Node<E> last; private static final long serialVersionUID = 876323262645176354L;
比ArrayList的成員變量少了好幾個呢。
初步分析:
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; } }
注意:此處只截取了Node的代碼,Node是LinkedList的靜態內部類,仍是在LinkedList.class文件內部的
分析:
也就是說,LinkedList的每個元素都是一個Node,而每個Node都儲存了三部份內容,由此也就證明了LinkedList是基於雙向鏈表的。
源碼以下所示:
public LinkedList() { } public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
分析:
能夠發現,相對於ArrayList,LinkedList類並無指定容量的構造,這是爲何呢?
思考:
1. 這就是ArrayList和LinkedList底層依賴不一樣有關係,ArrayList底層是數組,LinkedList底層是雙向鏈表。數組初始化是須要聲明長度的,鏈表則不須要。
2. 傳入子類進行構造時,也是調用了無參構造方法,再調用addAll()方法,將全部元素添加進去
源碼以下所示:
public void addFirst(E e) { linkFirst(e); } 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++; }
addFirst()方法是在鏈表頭部插入一個元素,分析以下:
源碼以下:
public boolean add(E e) { linkLast(e); return true; } 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++; }
add()方法默認是在鏈表的尾部進行添加元素。
分析:
源碼以下所示:
public E get(int index) { checkElementIndex(index); return node(index).item; }
get()方法內隱藏着LinkedList不便於進行遍歷的真相!必定要搞明白哦。
分析:
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; } }
分析以下:
能夠看出,當你訪問的元素越靠近鏈表的中間,那麼獲取該元素所花費的時間就會越長,因此LinkedList在遍歷上是比較慢的,鏈表自己是不支持任意性訪問的,雖然LinkedList的get()方法能夠讀到相應元素,可是效率很低,不建議使用。
源碼以下所示:
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; }
分析以下:
unlink方法源碼以下所示:
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; }
哇,unlink方法源代碼有點長啊,容我慢慢道來:
源碼以下所示:
public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); }
分析:
unlinkFirst()方法源碼以下所示:
private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
分析:
LinkedList能夠做爲FIFO(First In First Out)的隊列,也就是先進先出的隊列使用,如下是關於隊列的操做。
//獲取隊列的第一個元素,若是爲null會返回null public E peek() { final Node<E> f = first; return (f == null) ? null : f.item; } //獲取隊列的第一個元素,若是爲null會拋出異常 public E element() { return getFirst(); } //獲取隊列的第一個元素,若是爲null會返回null public E poll() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } //獲取隊列的第一個元素,若是爲null會拋出異常. public E remove() { return removeFirst(); } //將元素添加到隊列尾部 public boolean offer(E e) { return add(e); }
LinkedList也能夠做爲棧使用,棧的特性是LIFO(Last In First Out),也就是後進先出。 添加和刪除元素都只操做隊列的首節點便可。
源碼以下:
public boolean offerFirst(E e) { addFirst(e); return true; } public boolean offerLast(E e) { addLast(e); return true; } public E peekFirst() { final Node<E> f = first; return (f == null) ? null : f.item; } public E peekLast() { final Node<E> l = last; return (l == null) ? null : l.item; } public E pollFirst() { final Node<E> f = first; return (f == null) ? null : unlinkFirst(f); } public E pollLast() { final Node<E> l = last; return (l == null) ? null : unlinkLast(l); } public void push(E e) { addFirst(e); } public E pop() { return removeFirst(); } public boolean removeFirstOccurrence(Object o) { return remove(o); } public boolean removeLastOccurrence(Object o) { if (o == null) { for (Node<E> x = last; x != null; x = x.prev) { if (x.item == null) { //調用unlink方法刪除指定節點 unlink(x); return true; } } } else { for (Node<E> x = last; x != null; x = x.prev) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
LinkedList相對於ArrayList而言,源碼並無很複雜,從源碼中咱們得知了如下相關信息:
知之爲知之,不知爲不知,是知也。