基于双向链表的List实现,也实现了Deque接口,无界List。
1)非线程安全;
2)fail-fast:同ArrayList。
基于非环形双链表,维护first、last引用:
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node last;
// 双链表
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
若first=last=null,则链表为空,首尾节点的item不能为null。
// 无参构造
public LinkedList() {
}
// 指定Collection参数构造,将其中的元素添加进来
public LinkedList(Collection extends E> c) {
this();
addAll(c);
}
基础方法中涉及到链表首、尾、中间的插入与删除,都是关于Node的prev、next引用的操作,所以动态插入、删除都是很方便的。由于LinkedList维护了first、last引用,所以在链表首尾进行插入、删除是高效的,但是在链表中间插入、删除都需要先根据索引遍历获取到索引位置节点,所以有一个遍历成本。
// 如果链表只有一个节点,则first=last=node,其中node为节点
// 在链表头添加节点
private void linkFirst(E e) {
final Node f = first;
final Node newNode = new Node<>(null, e, f);
first = newNode; // 维护first,指向新增节点
if (f == null)
last = newNode; // 链表为空,维护last,指向新增节点
else
f.prev = newNode; // 将旧的链表头的prev指向新增节点
size++;
modCount++; // 结构性修改次数加1
}
// 在链表尾添加节点
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode; // 维护last,指向新增节点
if (l == null)
first = newNode; // 链表为空,维护first,指向新增节点
else
l.next = newNode; // 将旧的链表尾的next指向新增节点
size++;
modCount++;
}
// 在链表中间插入节点
void linkBefore(E e, Node succ) {
// assert succ != null;
final Node pred = succ.prev;
final Node newNode = new Node<>(pred, e, succ);
succ.prev = newNode; // 将succ的prev指向新增节点
if (pred == null)
first = newNode; // succ为旧的链表首节点,维护first,指向新增节点
else
pred.next = newNode; // 将新增节点插入进来
size++;
modCount++;
}
// 在链表首删除节点
private E unlinkFirst(Node f) {
// assert f == first && f != null;
final E element = f.item;
final Node next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null; // 链表为空,维护first、last
else
next.prev = null; // 更新链表首节点
size--;
modCount++;
return element;
}
// 在链表尾删除节点
private E unlinkLast(Node l) {
// assert l == last && l != null;
final E element = l.item;
final Node prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null; // 链表为空,维护first、last
else
prev.next = null; // 更新链表尾节点
size--;
modCount++;
return element;
}
// 在链表中间删除节点
E unlink(Node x) {
// assert x != null;
final E element = x.item;
final Node next = x.next;
final Node prev = x.prev;
if (prev == null) {
first = next; // 删除的节点为旧的链表首节点,维护first指向新的节点
} else {
prev.next = next; // 更新prev的next
x.prev = null; // help GC
}
if (next == null) {
last = prev; // 删除的节点为旧的链表尾节点,维护last指向新的节点
} else {
next.prev = prev; // 更新next的prev
x.next = null; // help GC
}
x.item = null; // 清空item
size--;
modCount++;
return element;
}
涉及到的方法有:
// List methods
public boolean add(E e)
public void add(int index, E element)
public boolean addAll(Collection extends E> c)
public boolean addAll(int index, Collection extends E> c)
// Deque methods
public void addFirst(E e)
public void addLast(E e)
public boolean offerFirst(E e)
public boolean offerLast(E e)
// Queue methods
public boolean offer(E e)
// Stack methods
public void push(E e)
由于LinkedList是无界的,关于添加元素的方法大同小异,都用到了基础方法来做。重点看下随机add、addAll:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
// 将index与一半容量比较,从first或last最快遍历
Node node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
public boolean addAll(int index, Collection extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index); // 根据索引遍历获取索引位置的节点
pred = succ.prev;
}
// 将数组a转化为一个双链表
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
随机插入都需要根据索引遍历获取索引位置的节点,然后在其前面插入,维护好prev、next引用。
涉及到的方法有:
// List methods
public E remove(int index)
public boolean remove(Object o)
// Deque methods
public E removeFirst()
public E removeLast()
public E pollFirst()
public E pollLast()
// Queue methods
public E remove()
public E poll()
// Stack methods
public E pop()
删除节点就是用到unlink。
来自List接口,这里是随机set:
public E set(int index, E element) {
checkElementIndex(index);
Node x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
涉及到的方法有:
// List methods
public E get(int index)
// Deque methods
public E getFirst()
public E getLast()
public E peekFirst()
public E peekLast()
// Queue methods
public E element()
public E peek()
public ListIterator listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator
在双链表的基础上实现了ListIterator,支持从索引位置遍历,另外还提供了与ListIterator遍历顺序相反的迭代器,利用了代理设计模式:
public Iterator descendingIterator() {
return new DescendingIterator();
}
private class DescendingIterator implements Iterator {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
List中的随机插入、删除、修改、访问都需要先根据索引遍历获取到索引位置的节点,再进行操作,有遍历成本,由于维护了first、last引用,所以在链表的首尾操作是高效的。