整體介紹
LinkedList同時實現了List接口和Deque接口,也就是說它既可以看作一個順序容器,又可以看作一個隊列(Queue),同時又可以看作一個棧(Stack)。
LinkedList底層實現是雙向鏈表,有一個頭指針和尾指針,當LinkedList為空時,頭指針和尾指針都指向null。
LinkedList和ArrayList一樣都用modCount實現了fail-fast機制。
如果想要線程同步,可以用
List list = Collections.synchronizedList(new LinkedList(...))LinkedList繼承自AbstractSequentialList,而AbstractSequentialList又繼承自AbstractList;LinkedList用的modCount就是來自AbstractList。
源碼分析
節點結構
LinkedList的單個節點結構如下,是一個雙向鏈表節點。
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;
}
}
成員變量
三個成員變量有transient關鍵字,
LinkedList復寫了readObject和writeObject方法,實現底層數組的序列化.-
first和last只有兩種狀態:
- LinkedList為空時:first == null && last == null
- LinkedList不為空時:
(first.prev == null && first.item != null) &&
(last.next == null && last.item != null)
//鏈表的大小
transient int size = 0;
/**
* 頭指針
*/
transient Node<E> first;
/**
* 尾指針
*/
transient Node<E> last;
get()
get()
方法分為檢查下標和獲取元素兩部分,獲取元素時根據index大小選擇從first指針還是last指針開始.
public E get(int index) {
//檢查下標
checkElementIndex(index);
//返回元素
return node(index).item;
}
/**
*檢查下標是否越界
*/
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
/**
*通過下標查找元素
*根據index是否大于size/2,選擇從頭指針開始還是尾指針開始,減少了時間耗費
*/
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;
}
}
set()
public E set(int index, E element) {
//檢查下標是否越界
checkElementIndex(index);
//找到下標為index的元素
Node<E> x = node(index);
//修改元素的值
E oldVal = x.item;
x.item = element;
return oldVal;
}
add()
add()會涉及到modCount++.
add()方法有兩個版本:
一個是add(E e),該方法在LinkedList的末尾插入元素,因為有last指向鏈表末尾,在末尾插入元素的花費是常數時間。只需要簡單修改幾個相關引用.
另一個是add(int index, E element),該方法是在指定下表處插入元素,需要先通過線性查找找到具體位置,然后修改相關引用完成插入操作。
public boolean add(E e) {
linkLast(e);
return true;
}
/**
*在鏈表末尾加入元素,last指向新元素.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
//如果last==null,說明此時LinkedList為空,有first和last都指向新元素
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public void add(int index, E element) {
//檢查下標是否越界;0<=index<=size
checkPositionIndex(index);
//如果index==size,則與add(E e)沒區別,直接調用linkLast(element);
if (index == size)
linkLast(element);
else
//先用node(index)把下標為index的元素找到
//再調用linkBefore把新元素插在找到的元素前面
//這樣新元素下標為index,找到的元素下標為index+1
linkBefore(element, node(index));
}
/**
*將e插在succ的前面
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
remove()
remove()會涉及到modCount++.
remove()方法有兩個版本,都是線性時間:
- remove(int index)
- 根據下標移除元素
- remove(Object o)
- 利用equals方法找到o對象,再移除
public E remove(int index) {
//檢查越界問題
checkElementIndex(index);
return unlink(node(index));
}
/**
*將x節點從鏈表中去除.
*/
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;
}
/**
*通過遍歷找到o節點后,用unlink移除o節點.
*/
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;
}
序列化
- 在上面成員變量那里提到了size,first和last用了transient關鍵字,無法序列化,這里ArrayList復寫了readObject和writeObject方法,實現底層鏈表的序列化.
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out size
s.writeInt(size);
// Write out all elements in the proper order.
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
// 將讀入的節點用linkLast方法放到鏈表尾部
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}
迭代器
LinkedList用
iterator()
可以返回迭代器.LinkedList的iterator()
繼承自AbstractSequentialList
,可以看到調用iterator()
返回的是
class ListItr implements ListIterator<E>
使用fail-fast機制,當有多個線程同時操作LinkedList,使用迭代器可以拋出錯誤,避免發生不同步。
基本的做法和ArrayList的差不多,代碼形式也類似,就不貼了。