LinkedList
概述
LinkedList 是 List 的一個實現, 底層使用雙向鏈表, 支持 O(1) 獲取頭節點和尾節點
底層使用雙向鏈表, 因此插入, 刪除速度較快, 查詢較慢。
雙向鏈表每次插入刪除節點時注意判斷頭尾節點,頭結點的 prev 爲空, 尾節點的 next 爲空
1. 類
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
......
}
繼承實現關係圖
- 繼承 AbstractSequentialList 抽象類, 實現了相關的 Collection 和 List的方法
- 實現了 List 接口
- 實現了 Clone 接口, 重寫了clone()方法
- 實現了 Deque 隊列接口, 可以把LinkedList 當做雙端隊列來操作
- 實現了 Serializable 接口, 可以序列化
2. 屬性
// LinkedList 包含的元素數量
transient int size = 0;
// 頭結點
transient Node<E> first;
// 尾節點
transient Node<E> last;
三個屬性都被標記成 transient 是因爲序列化和反序列化都需要遍歷整個鏈表. 該類寫了 writeObject() 和 readObject() 兩個方法來自定義實現序列化。
靜態內部類 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;
}
}
3. 方法
無參構造
public LinkedList() {
}
有參構造
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
add() addLast() linkLast()
public boolean add(E e) {
// 尾插法, 單獨封裝出來
linkLast(e);
return true;
}
public void addLast(E e) {
linkLast(e);
}
// 雙向鏈表尾插法
void linkLast(E e) {
final Node<E> l = last;
// 新節點構造, 直接設置 prev , next 和 item
final Node<E> newNode = new Node<>(l, e, null);
// 改變尾節點指針指向
last = newNode;
// 判斷上一節點是否爲空
if (l == null)
// 空表明該節點是頭結點
first = newNode;
else
// 非空設置上一節點的 next 指向新節點
l.next = newNode;
// 元素數量自增
size++;
modCount++;
}
addFirst() linkFirst()
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
// 非空則設置下一節點的 pre 等於新節點
f.prev = newNode;
size++;
modCount++;
}
addAll()
注意索引範圍檢查, 插入位置判斷, 頭尾節點的判斷修改,以及添加完後與之前的節點連接。
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
// 檢查index >= 0 && index <= size
checkPositionIndex(index);
// 取出 Collection 中的元素
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
// 如果要插入的地方是尾部, 則pred上一個節點就是尾節點last
if (index == size) {
succ = null;
pred = last;
} else {
// 如果不是, 則pred節點應該是index節點的上一個節點
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
// 轉爲泛型
@SuppressWarnings("unchecked") E e = (E) o;
// 構建新節點, 設置prev 節點和 item
Node<E> newNode = new Node<>(pred, e, null);
// 判斷上一個節點是否爲空
if (pred == null)
// 是空則表明該節點就是頭結點
first = newNode;
else
// 非空則設置上一個節點的 next 指向新節點
pred.next = newNode;
// 循環 新節點成爲pred
pred = newNode;
}
// 從尾部插入, 尾節點就是最後的pred節點
if (succ == null) {
last = pred;
} else {
// 不是從尾部開始插入, 那麼最後一個插入的節點應該與剩下的節點連接起來
pred.next = succ;
succ.prev = pred;
}
// 元素數量增加
size += numNew;
modCount++;
return true;
}
remove() unlink()
循環遍歷查找刪除, 注意 o.equals() 方法的空指針異常, unlink() 時注意頭尾節點的判斷以及前後節點指針的修改
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;
}
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;
// 如果刪除的是頭結點, 那麼重設頭結點爲next
if (prev == null) {
first = next;
} else {
// 如果刪除的不是頭結點, 那麼修改上個節點的next指向
prev.next = next;
x.prev = null;
}
// 刪除的是尾節點 那麼重設尾節點
if (next == null) {
last = prev;
} else {
// 如果不是, 那麼重設下一個節點的pre
next.prev = prev;
x.next = null;
}
// 引用釋放, 以便gc回收
x.item = null;
size--;
modCount++;
return element;
}
indexOf()
循環遍歷查找, 注意先判空, 防止調用 o.equals() 空指針
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;
}