LinkedList 是一個雙向鏈表,實現了 List 和 Deque 接口。它容許元素爲空,非線程安全。java
Deque 是一個雙端隊列,容許2端分別添加和刪除元素。JDK中的說明是這樣的node
A linear collection that supports element insertion and removal at both ends. The name deque is short for "double ended queue" and is usually pronounced "deck". Most
Deque
implementations place no fixed limits on the number of elements they may contain, but this interface supports capacity-restricted deques as well as those with no fixed size limit.數組
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList 區別於 ArrayList,它是繼承自 AbstractSequentialList,而且沒有實現 RandomAccess 接口。安全
若是集合類是 RandomAccess 的實現,儘可能用for(int i = 0; i < size; i++) 來遍歷而不要用Iterator迭代器來遍歷。數據結構
反過來,若是List是 Sequence List,則最好用迭代器來進行迭代。後面會看到若是對 LinkedList 使用普通的 for 循環,由於數據結構的緣由性能會不好。dom
AbstractSequentialList 的官方說明以下ide
This class provides a skeletal implementation of the List interface to minimize the effort required to implement this interface backed by a "sequential access" data store (such as a linked list). For random access data (such as an array), AbstractList should be used in preference to this class.性能
AbstractSequentialList 提供了一個順序訪問的集合,想要隨機訪問的話優先使用 AbstractList 的子類。ui
transient int size = 0; /** * Pointer to first node. */ transient Node<E> first; /** * Pointer to last node. */ transient Node<E> last; 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 內部類,包含數據體自己以及一個指向前面節點的指針和一個指向後面節點的指針。this
全局的指向頭和尾的 first last 指針。
LinkedList 內部是一個雙向鏈表結構。
public LinkedList() { } public LinkedList(Collection<? extends E> c) { this(); addAll(c); } public boolean addAll(Collection<? extends E> c) { return addAll(size, c); } public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index); Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node<E> pred, succ; if (index == size) { succ = null; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; }
提供了2個構造方法,無參的構造方法什麼事都不作,接收一個 collection 的構造方法將新的集合放到鏈表的尾端,將 last 指針指向最後一個節點。
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 方法調用的 linkLast 方法,方法很簡單 就是新建了一個node節點放到了鏈表的最後。
LinkedList大多都是鏈表操做,代碼也比較簡單,看下就懂了,下面稍微列出幾個方法。
// add first public void addFirst(E e) { linkFirst(e); } 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 last public void addLast(E e) { linkLast(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++; } // remove public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkFirst(Node<E> f) { 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; }
如開頭說的 AbstractSequentialList 不要使用隨機訪問的方式遍歷,應該採用迭代器或者foreach(foreach 底層是迭代器)。
由於 LinkedList 是個鏈表,沒法像數組同樣直接使用下標的方式取到對應的值。
// 隨機訪問的方式遍歷 int size = list.size(); for (int i=0; i<size; i++) { list.get(i); }
看一下它的get方法
public E get(int index) { checkElementIndex(index); return node(index).item; } Node<E> node(int 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; } }
經過 size >> 1 取到一個相對中間的值,而後比較 index 是在前面一段仍是後面一段,再按順序遍歷到指定的位置。畫蛇添足。
LinkedList 的大多數API都是基於一個雙向鏈表的操做,循環遍歷的時候採用迭代器或者foreach,避免使用普通的for循環。