本JDK源码阅读笔记基于OpenJDK 13 : https://github.com/openjdk/jdk

一些可能表述或理解不当,请见谅

1.概述

LinkedList 是一个双向链表。它也可以当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随即插入、随机删除效率高。

2.类图

LinkedList 实现的接口、继承的抽象类,如下图所示:

01.jpg

与ArrayList一致的接口:

  • java.util.List
  • java.io.Serialiable
  • java.lang.Cloneable

与ArrayList相比缺少的接口:

  • java.util.RandomAccess 即不支持随机访问

除此之外还实现了:

  • java.util.Deque 提供双端队列功能,LinkedList 支持快速的在头尾添加元素和读取元素,所以容易实现该特性
  • java.util.AbstractSequentialList

3.属性

LinkedListVsArray.png

LinkedList 一共有3个属性

  • Node 类就是代表双端链表的节点 Node 。这个类的三个属性分别是当前节点、前一个节点、后一个节点。
  • firstlast 属性分别代表链表的头节点和尾节点
  • size 属性表示链表的节点数量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 链表大小
*/
transient int size = 0;

/**
*
* 头节点
*
* Pointer to first node.
*/
transient Node<E> first;

/**
*
* 尾节点
*
* Pointer to last node.
*/
transient Node<E> last;
/**
*
* 节点
*
* @param <E> 元素泛型
*/
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;
}
}

4.构造方法

无参构造方法

1
2
public LinkedList() {
}

用已有的集合创建链表的构造方法

1
2
3
4
5
public LinkedList(Collection<? extends E> c) {
this();
// 添加c到链表中
addAll(c);
}

5.添加元素

5.1.添加单个元素

方法 #add(E e) 默认插入到末尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public boolean add(E e) {
// 添加末尾
linkLast(e);
return true;
}

void linkLast(E e) {
// <1>记录原last节点
final Node<E> l = last;
// <2>创建新节点
// 第一个参数表示, newNode的前一个节点为l
// 第二个参数表示, e为元素
// 第三个参数表示, newNode的后一个节点为null
final Node<E> newNode = new Node<>(l, e, null);
// <3>last指向新节点
last = newNode;
// <4>如果原last为null, 说明first也为空, 则first也指向新节点
if (l == null)
first = newNode;
// <5>如果原last非null, 说明first也非空, 则原last的next指向新节点
else
l.next = newNode;
// <6>增加链表大小
size++;
// <7>增加修改次数
modCount++;
}

5.2.添加单个元素到指定位置

方法 #add(int index, E element) 如果指定的位置就是size,直接添加到末尾;否则,获取指定位置的元素,并把元素添加到该元素前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
*
* 插入元素到指定位置
*
*/
public void add(int index, E element) {
// 校验不要超过范围
checkPositionIndex(index);

// 如果刚好等于链表大小, 直接添加到尾部即可
if (index == size)
linkLast(element);
// 添加到第index的节点前面
else
linkBefore(element, node(index)); // 获得第index位置的Node节点node
}

/**
*
* 获得第index位置的Node节点node
*
*/
Node<E> node(int index) {
// assert isElementIndex(index);

// 如果index小于size的一半, 就正序遍历, 获得第index个节点
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
// 如果index大于size的一半, 就倒序遍历, 获得第index个节点
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}

void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 获得succ的前一个节点
final Node<E> pred = succ.prev;
// 创建新的节点newNode
final Node<E> newNode = new Node<>(pred, e, succ);
// 设置succ的前一个节点为新节点
succ.prev = newNode;
// 如果pred为null, 说明first也为空, 则first指向newNode
if (pred == null)
first = newNode;
// 如果pred非null, 说明first也非空, 则pred也指向newNode
else
pred.next = newNode;
// 增加链表大小
size++;
// 增加修改次数
modCount++;
}

5.3.添加单个元素到链表的头尾

由于LinkedList实现了Deque接口,所以实现了#offerFirst(E e)#offerLast(E e)方法,分别添加元素到链表头尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean offerFirst(E e) {
addFirst(e);
return true;
}

public boolean offerLast(E e) {
addLast(e);
return true;
}

public void addFirst(E e) {
linkFirst(e);
}

public void addLast(E e) {
// 与上面的linkLast一样
linkLast(e);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void linkFirst(E e) {
// 记录原first节点
final Node<E> f = first;
// 创建新节点
final Node<E> newNode = new Node<>(null, e, f);
// first指向新节点
first = newNode;
// 如果原first为空, 说明last也为空, 则last也指向新节点
if (f == null)
last = newNode;
// 如果原first非空, 说明last也非空, 则原first的next指向新节点
else
f.prev = newNode;
// 增加链表大小
size++;
// 增加数组修改次数
modCount++;
}

5.4.添加多个元素

方法 #addAll(Collection<? extends E> c) 将集合从指定位置开始插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}

public boolean addAll(int index, Collection<? extends E> c) {
// 检查index范围
checkPositionIndex(index);

// 将c集合转换为数组
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0) // 如果无添加元素, 直接返回false
return false;

// 获取第index位置的节点succ和前驱节点pred
Node<E> pred, succ;
if (index == size) { // 如果index就是size, 说明插入到队尾, 所以succ为null, pred为last
succ = null;
pred = last;
} else { // 否则, succ是第index个节点, prev就是succ的前一个节点
succ = node(index);
pred = succ.prev;
}

// 遍历数组a, 将数据插入
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 创建新节点
Node<E> newNode = new Node<>(pred, e, null);
// 如果pred是null, 插入到头部位置
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}

// 修改succ和pred的指向
if (succ == null) { // 如果 succ 为 null , 说明插入队尾, 则直接修改 last 指向最后一个 pred
last = pred;
} else { // 如果 succ 非 null, 说明插入到 succ 的前面
pred.next = succ;
succ.prev = pred;
}

// 增加链表大小
size += numNew;
// 增加数组修改次数
modCount++;
return true;
}

6.移除元素

6.1.移除指定位置的元素

方法 #remove(int index) 找到第index个元素,调用#unlink(Node<E> x)方法进行移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public E remove(int index) {
checkElementIndex(index);
// 获得第index的Node节点, 然后进行移除
return unlink(node(index)); // node()获得第index的node节点
}

E unlink(Node<E> x) {
// assert x != null;
// 获得x的前后节点 prev,next
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;

// 将prev的next指向下一个节点
if (prev == null) { // 如果prev为空, 说明first被移除, 则直接将first指向next
first = next;
} else { // 如果prev非空
prev.next = next; // prev的next指向next
x.prev = null; // x的prev指向null
}

// 将next的prev指向上一个节点
if (next == null) { // 如果next为空, 说明last被移除, 则直接将last指向prev
last = prev;
} else { // 如果next非空
next.prev = prev; // next的prev指向prev
x.next = null; // x的next指向null
}

// 将x的item设置为null, 帮助GC
x.item = null;
// 减少链表大小
size--;
// 增加数组修改次数
modCount++;
return element;
}

6.2.移除指定元素

方法 #remove(Object o) 移除首个为 o 的元素,首先找到首个与 o 相等的节点,再调用#unlink(Node<E> x)进行移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public boolean remove(Object o) {
if (o == null) { // o为null的情况
// 顺序遍历, 找到null的元素后, 进行移除
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
// 顺序遍历, 找到等于o的元素后, 进行移除
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}

6.3.移除首个元素

方法 #remove()#removeFirst()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public E remove() {
return removeFirst();
}

public E removeFirst() {
final Node<E> f = first;
// 如果链表为空, 抛出异常
if (f == null)
throw new N oSuchElementException();
// 移除链表首个元素
return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
// 获得f的下一个节点
final Node<E> next = f.next;
// 设置f的item为null, 帮助GC
f.item = null;
// 设置f的next为null, 帮助GC
f.next = null; // help GC
// 修改first指向next
first = next;
// 修改next节点的prev指向null
if (next == null) // 如果链表只有一个元素, 说明被移除后, 队列就是空的, 则last设置为null
last = null;
else
next.prev = null;
// 链表大小减一
size--;
// 增加数组修改次数
modCount++;
return element;
}

另外还有实现Queue接口的几个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}

public E pop() {
return removeFirst();
}

public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}

6.4.移除最后一个元素

方法 #removeLast()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public E removeLast() {
final Node<E> l = last;
// 如果链表为空, 则抛出异常
if (l == null)
throw new NoSuchElementException();
// 移除链表的最后一个元素
return unlinkLast(l);
}

private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
// 获得f的上一个节点
final Node<E> prev = l.prev;
// 设置l的item为null, 帮助GC
l.item = null;
// 设置l的prev为null, 帮助GC
l.prev = null; // help GC
// 修改last指向prev
last = prev;
// 修改prev节点的next指向null
if (prev == null) // 如果链表只有一个元素, 说明被移除后, 队列就是空的, 则first设置为null
first = null;
else
prev.next = null;
// 链表大小减一
size--;
// 数组修改次数加一
modCount++;
return element;
}

另外还有实现Queue接口的#pollLast()方法,调用的#unlinkLast(Node<E> f)方法与上面一致

1
2
3
4
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}

6.5.移除多个元素

java.util.AbstractCollection<E>#removeAll(Collection<?> c) 方法,批量移除指定的多个元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
// 获得迭代器
Iterator<?> it = iterator();
// 通过迭代器遍历
while (it.hasNext()) {
// 如果c中存在该元素, 则进行移除
if (c.contains(it.next())) {
it.remove();
modified = true; // 标记修改
}
}
return modified;
}

相反的,#retainAll(Collection<?> c) 求LinkedList与指定集合的交集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
// 获得迭代器
Iterator<E> it = iterator();
// 通过迭代器遍历
while (it.hasNext()) {
// 如果c中不存在该元素, 则进行移除
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}

6.6.清空链表

方法 #clear()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
// 顺序遍历链表, 设置每个节点前后指向为null
// 通过这样的方式, 帮助GC
for (Node<E> x = first; x != null; ) {
// 获取下一个节点
Node<E> next = x.next;
// 设置x的item,next,prev为null
x.item = null;
x.next = null;
x.prev = null;
// 设置下一个节点为x
x = next;
}
// 清空first和last的指向
first = last = null;
// 设置链表大小为0
size = 0;
// 增加数组修改次数
modCount++;
}

7.查找、获取与设定指定元素

7.1.查找首个指定元素的位置

方法 #indexOf(Object o) 遍历查找首个为指定元素的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public int indexOf(Object o) {
int index = 0;
if (o == null) { // 如果o为null
// 顺序遍历, 如果item为null, 进行返回
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else { // 如果o非null
// 顺序遍历, 如果item为o的节点, 进行返回
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
// 未找到
return -1;
}

#contains(Object o) 基于该方法实现

1
2
3
public boolean contains(Object o) {
return indexOf(o) >= 0;
}

7.2.查找最后一个指定元素的位置

方法 #lastIndexOf(Object o) 倒序遍历查找最后一个指定元素的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public int lastIndexOf(Object o) {
int index = size;
if (o == null) { // o为null
// 倒序遍历, 如果item为null, 返回
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else { // o非null
// 倒序遍历, 如果item为o的节点, 返回
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
// 找不到
return -1;
}

7.3.获得指定位置的元素

由于 LinkedList 不支持随机访问,所以该方法时间复杂度为 O(n)

1
2
3
4
5
public E get(int index) {
checkElementIndex(index);
// 基于node()方法实现, 时间复杂度为O(n)
return node(index).item;
}

因为 LinkedList 实现了 Deque 接口,所以它实现了 #peekFirst()#peekLast() 方法,分别获得链表头、尾的元素

1
2
3
4
5
6
7
8
9
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;
}

因为LinkedList实现了 Queue 接口,所以它实现了 #peek() , #element() 方法,获取 LinkedList 的头节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}

public E element() { // 如果链表为空, 抛出NoSuchElementException异常
return getFirst();
}

public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}

另外还有 #getLast() 方法,获取 LinkedList 的尾节点

1
2
3
4
5
6
7

public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}

7.4.设置指定位置的元素

方法 #set(int index, E element) ,设定指定元素到指定位置

1
2
3
4
5
6
7
8
9
10
public E set(int index, E element) {
checkElementIndex(index);
// 获取第index位置的节点
Node<E> x = node(index);
// 获取节点原来的值
E oldVal = x.item;
// 修改对应的值
x.item = element;
return oldVal;
}

8.序列化与反序列化

8.1.序列化

方法 #writeObject(java.io.ObjectOutputStream s) ,序列化链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@java.io.Serial
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
// 写入非静态属性、非transient属性
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);
}

8.2.反序列化

方法 #readObject(java.io.ObjectInputStream s) ,反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@SuppressWarnings("unchecked")
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
// 读取非静态属性、非transient属性
s.defaultReadObject();

// Read in size
// 读取size
int size = s.readInt();

// Read in all elements in the proper order.
// 顺序遍历, 逐个反序列化
for (int i = 0; i < size; i++)
linkLast((E)s.readObject()); // 添加到链表尾部
}

99.其他常用方法

99.1.转换为数组

方法 #toArray() ,将 LinkedList 转换为 Object 数组

1
2
3
4
5
6
7
8
9
public Object[] toArray() {
// 创建Object数组
Object[] result = new Object[size];
// 顺序遍历节点, 设置到Object数组中
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}

如果需要指定类型,可以用 toArray(T[] a) 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// 如果传入的数组小于size大小, 则直接复制一个新数组
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
// 顺序遍历链表, 复制到a中
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;

// 如果传入的数组大于size大小, 则将size赋值为null
if (a.length > size)
a[size] = null;

// 返回a
return a;
}

99.2.求哈希值

java.util.AbstractList<E>#hashCode() 方法

1
2
3
4
5
6
7
public int hashCode() {
int hashCode = 1;
// 遍历, 求哈希
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}

99.3.判断相等

java.util.AbstractList<E>#equals(Object o) 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public boolean equals(Object o) {
// 如果o就是自己, 返回true
if (o == this)
return true;
// 如果o不是List, 返回false
if (!(o instanceof List))
return false;

// 创建迭代器, 顺序遍历比对
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2))) // 如果不相等, 返回false
return false;
}
// 如果迭代器没有遍历完, 说明两者长度不等, 所以不相等; 否则, 相等
return !(e1.hasNext() || e2.hasNext());
}

99.4.克隆

方法 #clone() ,克隆 LinkedList 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Object clone() {
// 调用父类, 进行克隆
LinkedList<E> clone = superClone();

// Put clone into "virgin" state
// 重置clone为初始化状态
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;

// Initialize clone with our elements
// 顺序遍历, 逐个添加到clone中
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);

return clone;
}

本文作者: Kamadev
      本文地址: https://ckamadev.github.io/2020/04/07/JDK源码阅读笔记——集合Ⅱ链表LinkedList/
      版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!