本文基於 JDK8node
LinkedList 底層經過雙向集合的數據結構實現git
LinkedList 能夠做爲 List 使用,也能夠做爲隊列和棧使用。支持從集合的頭部,中間,尾部進行添加、刪除等操做。github
LinkedList 的繼承與實現的關係圖以下所示。數組
如下說明摘自 JDK 文檔。微信
Node 是 LinkedList 的私有內部類,是 LinkedList 的核心,是 LinkedList 中用來存儲節點的類,E 符號爲泛型,屬性 item 爲當前的元素,next 爲指向當前節點下一個節點,prev 爲指向當前節點上一個節點,是一種雙集合結構。數據結構
/**
* Node 內部類
* @param <E>
*/
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;
}
}複製代碼
/**
* 序列號
*/
private static final long serialVersionUID = 876323262645176354L;
/**
* 集合的長度
*/
transient int size = 0;
/**
* 集合頭節點。
* Invariant: (first == null && last == null) || (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* 集合尾節點
* Invariant: (first == null && last == null) || (last.next == null && last.item != null)
*/
transient Node<E> last;複製代碼
無參構造器,構造一個空集合。函數
/**
* 構造一個空集合
*/
public LinkedList() {
}複製代碼
構造一個包含指定集合元素的集合,其順序由集合的迭代器返回。源碼分析
/**
* 構造一個包含指定集合元素的集合,其順序由集合的迭代器返回。
*
* @param c 指定的集合
* @throws NullPointerException 集合爲空拋出
*/
public LinkedList(Collection<? extends E> c) {
// 調用無參構造函數進行初始化
this();
// 將集合 c 添加進集合
addAll(c);
}複製代碼
將指定的元素添加到集合的末尾,該方法和 addLast 方法的做用同樣,主要是經過 linkLast 方法來實現插入到末尾,步驟如圖所示。性能
/**
* 將指定的元素添加到集合的末尾
* 此方法和 addLast 方法的做用同樣
*
* @param e 添加的元素
* @return 返回添加成功
*/
public boolean add(E e) {
// 調用 linkLast 方法插入元素
linkLast(e);
return true;
}
/**
* 將指定的元素添加到集合的末尾
* 此方法和 add 方法的做用同樣
*/
public void addLast(E e) {
linkLast(e);
}
/**
* 將該元素添加到集合的末尾
*/
void linkLast(E e) {
// 獲取舊尾節點
final Node<E> l = last;
// 構建一個新節點,該新節點的上一個節點指向舊尾節點,下一個節點爲 null
final Node<E> newNode = new Node<>(l, e, null);
// 將新節點更新到尾節點
last = newNode;
// 若是舊尾節點爲空,則該新節點既是首節點也是尾節點
if (l == null)
first = newNode;
else
// 舊尾節點不爲空的話,將舊尾節點的下一個節點指向新尾節點
l.next = newNode;
// 集合長度 +1
size++;
// 修改次數 +1,適用於 fail-fast 迭代器
modCount++;
}複製代碼
將該元素添加到集合的頭部,主要經過調用 linkFirst 方法來實現,步驟如圖所示。this
/**
* 將該元素添加到集合的頭部
*
* @param e 添加的元素
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 將該元素添加到集合的頭部
*/
private void linkFirst(E e) {
// 獲取集合舊首節點
final Node<E> f = first;
// 構建一個新節點,該新節點的下一個節點是舊首節點,上一個節點爲 null
final Node<E> newNode = new Node<>(null, e, f);
// 將新節點更新到首節點
first = newNode;
// 若是舊首節點爲空,則該新節點既是首節點也是尾節點
if (f == null)
last = newNode;
else
// 將舊首節點的上一個節點指向新首節點
f.prev = newNode;
// 集合長度 +1
size++;
// 修改次數 +1
modCount++;
}
複製代碼
將指定的元素插入集合中的指定位置。 將當前在該位置的元素(若是有的話)和任何後續的元素向右移位。在集合中間插入元素的平均時間複雜度爲 O(1),該方式主要經過 node(int index) 方法找到對應位置的節點,再經過 linkBefore(E e, Node
/**
* 將指定的元素插入集合中的指定位置
*
* @param index 插入的指定位置
* @param element 插入的元素
* @throws IndexOutOfBoundsException 下標越界拋出
*/
public void add(int index, E element) {
// 檢查 index 是否越界
checkPositionIndex(index);
// 若是 index == size,則調用 linkLast 方法將該元素插入到最後一個
if (index == size)
linkLast(element);
else
// 將該節點插入到原來 index 位置的節點以前
linkBefore(element, node(index));
}
/**
* 檢查下標是否越界
*
* @param index
*/
private void checkPositionIndex(int index) {
// isPositionIndex 返回 false 則拋出 IndexOutOfBoundsException
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 檢查下標是否爲迭代器或加法操做的有效位置的索引
*/
private boolean isPositionIndex(int index) {
// 下標小於 0 或 大於 size 返回 false
return index >= 0 && index <= size;
}
/**
* 構建 IndexOutOfBoundsException 異常的詳細信息
*/
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
/**
* 在非 null 節點 succ 以前插入元素 e
* succ 節點爲下標 index 所在的節點,將包括該節點和該節點後面的節點日後移
*/
void linkBefore(E e, Node<E> succ) {
// 獲取 succ 節點的上一個節點
final Node<E> pred = succ.prev;
// 構造新節點,該新節點的上一個節點爲 pred,下一個節點爲 succ
final Node<E> newNode = new Node<>(pred, e, succ);
// 將 succ 的前一個節點指向新節點
succ.prev = newNode;
// 若是 pred 節點爲空,則插入的新節點指向首節點
if (pred == null)
first = newNode;
else
// pred 節點不爲空,則 pred 節點的下一個節點指向新節點
pred.next = newNode;
// 集合長度 +1
size++;
// 修改次數 +1
modCount++;
}複製代碼
刪除集合的第一個節點,並返回該元素,和 removeFirst 方法的做用同樣,主要經過 unlinkFirst 方法實現刪除頭節點,並返回頭節點的值,刪除節點時將對應的節點值和節點的指向都置爲了 null,方便 GC 回收。刪除步驟如圖所示。
/**
* 刪除集合的第一個節點,並返回該元素
*
* @return 返回集合頭節點
* @throws NoSuchElementException 集合爲空拋出
*/
public E remove() {
return removeFirst();
}
/**
* 刪除集合的第一個節點,並返回該元素
*
* @return 返回集合頭節點
* @throws NoSuchElementException 集合爲空拋出
*/
public E removeFirst() {
// 獲取首節點
final Node<E> f = first;
// 沒有首節點拋出 NoSuchElementException
if (f == null)
throw new NoSuchElementException();
// 刪除首節點
return unlinkFirst(f);
}
/**
* 刪除不爲 null 的首節點
*/
private E unlinkFirst(Node<E> f) {
// 獲取舊首節點的值
final E element = f.item;
// 獲取舊首節點的下一個節點 next,next 爲新首節點
final Node<E> next = f.next;
// 將舊首節點的值和下一個節點的指向賦爲 null,幫助 GC
f.item = null;
f.next = null;
// 用新首節點 next 更新首節點 first
first = next;
// 若是 next 節點爲空,則表明原集合只有一個節點,將尾節點也指向 null
if (next == null)
last = null;
else
// next 不爲空的話,將該新首節點的上一個節點指向 null
next.prev = null;
// 集合長度 -1
size--;
// 修改次數 +1
modCount++;
// 返回刪除的首節點元素
return element;
}複製代碼
刪除集合的最後一個節點,並返回該元素,主要經過 unlinkLast方法實現刪除尾節點,並返回尾節點的值,刪除步驟如圖所示。
/**
* 刪除尾節點並返回該元素
*
* @return 返回尾節點
* @throws NoSuchElementException 集合爲空拋出
*/
public E removeLast() {
// 獲取尾節點
final Node<E> l = last;
// 尾節點爲 null 拋出 NoSuchElementException
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 刪除不爲 null 的尾節點
*/
private E unlinkLast(Node<E> l) {
// 獲取舊尾節點的值
final E element = l.item;
// 獲取舊尾節點的上一個節點 prev,prev 爲新尾節點
final Node<E> prev = l.prev;
// 將舊尾節點的值和下一個節點的指向置爲 null,幫助 GC
l.item = null;
l.prev = null;
// 用新尾節點 prev 更新尾節點 last
last = prev;
// 若是新尾節點節點爲空,則表明該集合只有一個節點,將首節點指向 null
if (prev == null)
first = null;
else
// 新尾節點不爲空,將新尾節點的下一個節點指向 null
prev.next = null;
// 集合長度 -1
size--;
// 修改次數 +1
modCount++;
// 返回刪除的尾節點的值
return element;
}複製代碼
刪除集合中指定位置的元素。將全部後續元素向前移動,並返回從集合中刪除的元素,先經過 node(int index) 方法獲取指定位置的節點,再經過 unlink(Node
/**
* 刪除集合中指定位置的元素。將全部後續元素向前移動,並返回從集合中刪除的元素
*
* @param index 刪除的位置
* @return 返回刪除位置的元素
* @throws IndexOutOfBoundsException 索引越界拋出
*/
public E remove(int index) {
// 檢查是否越界
checkElementIndex(index);
// 刪除指定下標所在的節點
return unlink(node(index));
}
/**
* 刪除指定不爲 null 的節點
*/
E unlink(Node<E> x) {
// 獲取指定節點的值,用於最後返回
final E element = x.item;
// 獲取該節點的下一個節點 next
final Node<E> next = x.next;
// 獲取該節點的上一個節點 prev
final Node<E> prev = x.prev;
// 若是 prev 節點爲 null,表示該節點爲首節點,將 next 節點指向首節點
if (prev == null) {
first = next;
} else {
// prev 節點不爲 null,則將 prev 的下一個節點指向 next
prev.next = next;
// 將該節點的上一個節點置爲 null,幫助 GC
x.prev = null;
}
// 若是 next 節點爲 null,表示該節點爲尾節點,將 prev 節點指向尾節點
if (next == null) {
last = prev;
} else {
// next 節點不爲 null,將 next 的上一個節點指向 prev
next.prev = prev;
// 將該節點的下一個節點置爲 null,幫助 GC
x.next = null;
}
// 將該節點的值置爲 null
x.item = null;
// 集合長度 -1
size--;
// 修改次數 +1
modCount++;
// 返回刪除的元素的值
return element;
}複製代碼
返回集合中指定位置的元素,先檢查下標是否越界,再經過 node(index) 方法取到對應下標的節點,該節點的 item 屬性即爲對應的值。
/**
* 返回集合中指定位置的元素
*
* @param index 指定位置
* @return 返回的元素
* @throws IndexOutOfBoundsException 下標越界拋出
*/
public E get(int index) {
// 檢查下標越界
checkElementIndex(index);
// node(index) 返回節點
return node(index).item;
}
/**
* 返回集合中的第一個元素
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 返回集合中的最後一個元素
*
* @return
* @throws NoSuchElementException 集合爲空拋出異常
*/
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}複製代碼
返回指定元素索引處的(非空)元素,不少方法都會涉及到該方法。
/**
* 返回指定元素索引處的(非空)元素
*/
Node<E> node(int index) {
// 判斷 index 更接近 0 仍是 size 來決定從哪邊遍歷
if (index < (size >> 1)) {
// 從首節點遍歷
// 獲取首節點
Node<E> x = first;
// 從首節點日後遍歷 index 次,獲取到 index 下標所在的節點
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
// 從尾節點遍歷
// 獲取尾節點
Node<E> x = last;
// 從首節點往前遍歷 size-index-1 次,獲取到 index 下標所在的節點
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}複製代碼
將指定的元素添加到集合的尾部,該方法的做用和 add(E e) ,addLast(E e) 同樣。當使用容量受限的雙端隊列時,此方法一般比 add 方法更可取,當超出隊列容量時,該方法會返回 false,而 add 方法則會拋出異常。
/**
* 將指定的元素添加到集合的尾部
* @param e 須要插入的元素
* @return 返回是否插入成功
*/
public boolean offer(E e) {
return add(e);
}
/**
* 插入特定元素到集合末尾,該方法和 addLast 做用同樣
*
* @param e 插入的元素
* @return 返回 true
*/
public boolean offerLast(E e) {
addLast(e);
return true;
}
/**
* 將元素插入到集合的頭部
*
* @param e 插入的元素
* @return 返回 true
*/
public boolean offerFirst(E e) {
addFirst(e);
return true;
}複製代碼
刪除集合的頭節點,該方法的做用和 remove()同樣,不過當兩個方法對空集合使用時,remove() 方法會拋出異常,而 poll() 方法會返回 null。
pollFirst() 方法和 poll() 方法的做用同樣,當集合爲空時返回 null。
pollLast() 方法和 removeLast() 方法的做用同樣,不過當集合爲空時,pollLast() 方法回 null,removeLast() 方法拋出異常。
/**
* 刪除集合的頭節點
*
* @return 返回 null 或頭節點元素
*/
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* 刪除集合頭節點
*
* @return 返回 null 或集合的一個元素
*/
public E pollFirst() {
// 獲取首節點
final Node<E> f = first;
// 若是該集合沒有元素則返回 null,不然刪除首節點
return (f == null) ? null : unlinkFirst(f);
}
/**
* 檢刪除鏈表的最後一個元素
*
* @return 返回 null 或集合最後一個元素
*/
public E pollLast() {
// 獲取尾節點
final Node<E> l = last;
// 若是該集合沒有元素則返回 null,不然刪除尾節點
return (l == null) ? null : unlinkLast(l);
}複製代碼
peek() 方法的做用和 getFirst() 方法同樣,不過當集合爲空時,peek() 方法返回 null,而 getFirt() 方法拋出異常。
peekFirst() 方法的做用和 peek() 同樣,peekLast() 方法的做用和 removeLast() 方法同樣,不過該方法遇到空集合也是返回 null。
/**
* 返回集合的第一個元素
*
* @return
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* 返回集合的第一個元素,做用和 peek() 同樣
*
* @return
*/
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* 返回集合的最後一個元素
*
* @return
*/
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}複製代碼
文章同步到公衆號和Github,有問題的話能夠聯繫做者。