概覽
同 ArrayList 同樣,LinkedList 也是對 List 接口的一種具體實現。不一樣的是,ArrayList 是基於數組來實現的,而 LinkedList 是基於雙向鏈表實現的。LinkedList 類的聲明以下:java
1 2 3
|
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
|
LinkedList 繼承自 AbstractSequentialList,實現了 List 接口,同時還實現了 Deque 接口。AbstractSequentialList 是 AbstractList 的子類,爲基於順序訪問的鏈表提供了一些基本的實現。LinkedList 實現了 Deque 接口,Deque 接口是一種雙端隊列,能夠做爲 FIFO 隊列和 LIFO 隊列(棧)來使用。LinkedList 支持全部元素,包括 null。node
下面基於JDK 8 中的代碼對LinkedList的實現加以分析。數組
底層結構
LinkedList 基於雙向鏈表進行實現,並使用兩個引用 first 和 last 分別指向雙向鏈表的頭尾元素。同 ArrayList 同樣,使用 modCount 來記錄結構化修改的次數,並依此實現 fail-fast 機制。併發
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
transient int size = 0;
|
雙向鏈表的節點結構以下,分別用 prev 和 next 指向該節點的前驅和後繼結點。ui
1 2 3 4 5 6 7 8 9 10 11
|
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; } }
|
雙向鏈表操做
LinkedList 提供的全部操做都是在雙向鏈表的基礎上完成的。Dqeue 接口的一些實現就是在 雙向鏈表的兩端進行操做,也是基於對頭和尾部元素的操做。總的來講,雙向鏈表的操做並不複雜,下面簡單地進行解析,大部分操做都是對如下幾種操做的封裝。this
向頭部添加元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
|
向尾部添加元素
1 2 3 4 5 6 7 8 9 10 11
|
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++; }
|
從中間插入元素
移除頭部節點
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
|
移除尾部節點
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
private E unlinkLast(Node<E> l) { |
移除任意一個非空節點
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
|
清空鏈表
主要是爲了方便垃圾回收器進行垃圾回收。spa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
public void clear() { |
根據索引獲取元素
由於是雙向鏈表,可向前遍歷,也可向後遍歷。查找時雙向進行,靠近頭節點則從前向後查找;靠近尾部,則從後向前查找。code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
|
反查一個元素的索引
第一次出現:繼承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public int indexOf(Object o) { int index = 0; if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; }
|
最後一次出現,從後向前查找:索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
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; }
|
迭代器
經過 next 的指向依次進行遍歷。還提供了反向的迭代(從尾部到頭部),經過 prev 的指向依次遍歷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
private class ListItr implements ListIterator<E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; |
小結
LinkedList 是 List 接口基於雙向鏈表的一種實現,同時還實現了 Deque 接口,能夠做爲 FIFO 和 LIFO 隊列使用。雙向鏈表的實現使得插入和刪除操做的代價下降,能夠在常數時間內完成;然而查找操做須要遍歷列表,儘管雙向列表使得能夠從兩端進行查找,但在長度較長時仍然須要較長的時間。
在大多數狀況下會選擇使用 ArrayList,儘管插入和刪除代價相較於 LinkedList 更高,但隨機訪問的特性使得在查找方面 ArrayList 比 LinkedList 具備更多的優點。關於 ArrayList 和 LinkedList 的使用選擇上能夠參考 StackOverflow 上的這個問答。