目录
1.概述
List和Deque接口的双向链表实现。 实现所有可选的列表操作,并允许所有元素(包括null )。继承了AbstractSequentialList接口,实现了Cloneable接口,是可克隆的,实现了Serializable接口,是可序列化的,实现了Deque接口,List接口,具有Deque双向队列与list链表的特性。是线程不安全的,没有同步实现
注意,虽然LinkedList实现了Deque接口,但是LinkedList大部分Deque不太一样:
a) 一般Deque不允许null值,但LinkedList允许null值
b) 一般Deque不限容量,但LinkedList有最大容量:Integer.MAX_VALUE
2.属性
//成员变量
//链表长度
transient int size = 0;
//指向头节点的指针(最初为null)
transient Node<E> first;
//尾结点
transient Node<E> last;
//重要的私有静态内部类
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);
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
//从指定位置开始,将指定集合中的所有元素插入此列表。 将当前在该位置的元素(如果有)和任何后续元素右移(增加其索引)。 新元素将按照指定集合的迭代器返回的顺序显示在列表中
public boolean addAll(int index, Collection<? extends E> c) {
//判断索引是否越界
checkPositionIndex(index);
Object[] a = c.toArray();
//新加入集合的长度
int numNew = a.length;
if (numNew == 0)
return false;
//pred前驱节点,succ为后继节点
Node<E> pred, succ;
if (index == size) {//当在链表最后插入时,last变为前节点,后继节点为空
succ = null;
pred = last;
} else {//在当前节点的前面插入整个数组.
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
//新建包含集合内内容的节点,并依次连接到原链表中
@SuppressWarnings("unchecked") E e = (E) o;
//新建一个前驱为尾结点,后继为null的节点
Node<E> 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;
}
4.链表相关操作
4.1获取元素
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//返回指定元素索引处的(非空)节点
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;
}
}
4.2增加元素
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
linkLast(e);
return true;
}
//将指定的元素插入此列表中的指定位置。 将当前在该位置的元素(如果有)和任何后续元素右移(将其索引加一)
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);//尾插
else
linkBefore(element, node(index));
}
//尾插法
void linkLast(E e) {
final Node<E> l = last;
//建立前驱为旧链表的尾节点,后继为空的新节点
final Node<E> newNode = new Node<>(l, e, null);
//更新尾结点
last = newNode;
if (l == null)
first = newNode;
else//将旧链表的后继指向新节点,完成增加新节点操作
l.next = newNode;
size++;
modCount++;
}
//在非null节点succ之前插入元素e,过程很简单,不做过多解读
void linkBefore(E e, Node<E> succ) {
//succ不为空
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++;
}
4.3删除操作
删除指定元素:如果存在指定元素,则从该列表中删除该元素的第一次出现。 如果此列表不包含该元素,则它保持不变。 更正式地,删除具有最低索引i的元素,以使Objects.equals(o, get(i)) (如果存在这样的元素)。 如果此列表包含指定的元素,则返回true (或者等效地,如果此列表由于调用而更改),则返回true 。
//删除指定元素
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;
}
删除指定索引位置元素:
public E remove(int index) {
//检查索引是否符合规范
checkElementIndex(index);
//node(index)为获取索引index处的节点,见获取元素中对该方法的详解
return unlink(node(index));
}
//删除指定元素节点,节点不为空
E unlink(Node<E> x) {
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;
}
5.Deque相关操作
5.1插入
//头插
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
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
f.prev = newNode;
size++;
modCount++;
}
//尾插
public boolean offerLast(E e) {
addLast(e);
return true;
}
public void addLast(E e) {
linkLast(e);
}
5.2删除
//移除队列头
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
//或
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
//移除操作
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
//移除队列尾
...
5.3其余操作
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
6.克隆机制(浅拷贝)
public Object clone() {
LinkedList<E> clone = superClone();
// 将克隆置于“原始”状态
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// 用我们的元素初始化克隆
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
7.序列化机制
其中的很多成员变量都不允许序列化,但是其继承了Serializable接口,并实现了自己的序列化方法writeObject与反序列方法readObject。
//将此LinkedList实例的状态保存到流中(即,对其进行序列化)
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// 将当前类的非静态和非瞬态字段写入此流。 只能从要序列化的类的writeObject方法中调用此方法。 如果否则调用它将抛出NotActiveException。
s.defaultWriteObject();
//写出容量大小
s.writeInt(size);
// 按照正确的顺序写出所有元素
for (Node<E> x = first; x != null; x = x.next)
s.writeObject(x.item);
}
//从流中重构此LinkedList实例(即,对其进行反序列化)
@SuppressWarnings("unchecked")
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 从此流中读取当前类的非静态和非瞬态字段。 只能从要反序列化的类的readObject方法中调用此方法。 如果否则调用它将抛出NotActiveException
s.defaultReadObject();
// 读取大小
int size = s.readInt();
// 按照正确的顺序读出所有元素
for (int i = 0; i < size; i++)
linkLast((E)s.readObject());
}