整体介绍
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的差不多,代码形式也类似,就不贴了。