源码解析-LinkedList
# 概述
LinkedList继承图如下
可以看到LinkedList同时实现了List接口和Deque接口,也就是说它既可以看作一个顺序容器,又可以看作一个队列(Queue),同时又可以看作一个栈(Stack)。这样看来,LinkedList简直就是个全能冠军。当你需要使用栈或者队列时,可以考虑使用LinkedList,一方面是因为Java官方已经声明不建议使用Stack类,更遗憾的是,Java里根本没有一个叫做Queue的类(它是个接口名字)。关于栈或队列,现在的首选是ArrayDeque,它有着比LinkedList(当作栈或队列使用时)有着更好的性能。
LinkedList的实现方式决定了所有跟下标相关的操作都是线性时间,而在首段或者末尾删除元素只需要常数时间。为追求效率LinkedList没有实现同步(synchronized),如果需要多个线程并发访问,可以先采用Collections.synchronizedList()
方法对其进行包装。
LinkedList
一些主要特性:
LinkedList
集合底层实现的数据结构为双向链表LinkedList
集合中元素允许为 nullLinkedList
允许存入重复的数据LinkedList
中元素存放顺序为存入顺序。LinkedList
是非线程安全的,如果想保证线程安全的前提下操作LinkedList
,可以使用List list = Collections.synchronizedList(new LinkedList(...));
来生成一个线程安全的LinkedList
# 底层数据结构
链表是一种不同于数组的数据结构,双向链表是链表的一种子数据结构,它具有以下的特点:
每个节点上有三个字段:当前节点的数据字段(data),指向上一个节点的字段(prev),和指向下一个节点的字段(next)。
LinkedList底层通过双向链表实现,本节将着重讲解插入和删除元素时双向链表的维护过程,也即是直接跟List接口相关的函数,而将Queue和Stack以及Deque相关的知识放在下一节讲。双向链表的每个节点用内部类Node表示。LinkedList通过first
和last
引用分别指向链表的第一个和最后一个元素。当链表为空的时候first
和last
都指向null
。
//Node内部类
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;
}
}
2
3
4
5
6
7
8
9
10
11
# 构造函数
//元素数量
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
指向整体链表的第一个元素
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
//指向链表的第二个元素
*/
transient Node<E> last;
/**
* Constructs an empty list.
无参构造器
*/
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
传入一个集合的构造器
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
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
# 核心方法源码解析
# add()
add方法有两种,实现略微不同,一种是add(E e),另外一个方法是在指定位置添加元素 add(int index E e),我们先看 add(E e)。
add(E e)是在当前链表的最后端直接插入一个元素,源码比较简单,具体步骤参考注释
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
//插入尾部
linkLast(e);
//直接返回true
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
//获取last节点,需要一个单独变量 赋值一下
final Node<E> l = last;
//把新插入的元素新建节点,具体参考 Node这个类的定义 l:前一个元素 e:当前元素 null:下一个元素
final Node<E> newNode = new Node<>(l, e, null);
//把last指向 新的元素节点
last = newNode;
//l == null表示 链表为空的情况下,需要顺便给first单独赋值,last已经在上一步赋值了
if (l == null)
first = newNode;
else
l.next = newNode;//链表不是空的情况下,需要把l的next指向当前的last(即新建的newNode节点),last的pre在new Node()的时候已经指向了l节点了
size++;//当前存储的size+1
modCount++; //修改次数+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
add(int index,E e) 是在指定位置插入一个元素,实现方式是如果 index == size 那就跟add(E e)逻辑保持一致,如果index是中间某个坐标,那就是修改对应位置的node的前后节点的引用来达到目的,具体可以看代码注释。代码中的node(int index)
函数有一点小小的trick,因为链表双向的,可以从开始往后找,也可以从结尾往前找,具体朝那个方向找取决于条件index < (size >> 1)
,也即是index是靠近前端还是后端。从这里也可以看出,linkedList通过index检索元素的效率没有arrayList高。
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
//check下避免index超出整体 size范围
checkPositionIndex(index);
//如果插入的index是最后,那跟add(E e)逻辑保持一致
if (index == size)
linkLast(element);
else
//插入中间某个节点
linkBefore(element, node(index));
}
/**
* Returns the (non-null) Node at the specified element index.
这个方法目的是返回指定index的node
*/
Node<E> node(int index) {
// assert isElementIndex(index);
//比较trick的地方,如果index < size/2 那就从前往后遍历查找,否则从后往前遍历,也就是说 靠前就从前遍历,靠后就从后遍历,提升效率
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;
}
}
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
//获取到 前置节点
final Node<E> pred = succ.prev;
//新建需要插入的节点,pre指向前置节点,后置节点指向 找到的succ节点
final Node<E> newNode = new Node<>(pred, e, succ);
//succ的前置节点修改为 新节点
succ.prev = newNode;
//插入位置为0
if (pred == null)
first = newNode;
else
pred.next = newNode; //前置节点的 next指向新节点
size++;
modCount++;
}
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
# remove()
remove操作也分两种 remove(Object o),remove(int index)。remove(Object o)实现的是移除 o这个元素,remove(int index)实现的是移除指定index上的元素。实现比较简单,具体直接参考注释即可,这里给大家建议,链表的代码看起来会比较绕,可以对着代码在草稿本上画图来看整个代码逻辑会比较清晰。
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
//check下index是否越界
checkElementIndex(index);
return unlink(node(index));
}
/**
* Returns the (non-null) Node at the specified element index.
这个方法目的是返回指定index的node,具体注释参考 add方法中的介绍
*/
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;
}
}
/**
* Unlinks non-null node x.
移除指定node。假设链表是 a -> node ->b ,这个方法做的就是把 a的next指向b,然后 b的pre指向a,最后形成 a->b ,中间很多代码都是各种特殊情况处理
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
//获取 后置节点 next
final Node<E> next = x.next;
//获取 前置节点 prev
final Node<E> prev = x.prev;
if (prev == null) {
//移除的是头部节点,需要把first变量重新赋值
first = next;
} else {
//前置节点 next 指向next节点
prev.next = next;
//解除 需要移除的节点的pre的联系
x.prev = null;
}
if (next == null) {
//移除的节点是尾部节点,需要堆last重新赋值
last = prev;
} else {
//后置节点的prev 指向 前置节点
next.prev = prev;
//解除需要移除的节点 next指向关系
x.next = null;
}
//把x的item 赋为null,然后gc回收
x.item = null;
//size -1
size--;
//修改次数+1
modCount++;
//返回被移除的元素
return element;
}
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns {@code true} if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
public boolean remove(Object o) {
// 需要移除node的值为null的节点
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) {
//节点比较是否相等 使用的equals
if (o.equals(x.item)) {
//找到具体节点以后 移除
unlink(x);
return true;
}
}
}
return false;
}
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# get()
实现比较简单,参考注释
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
//获取指定的元素 然后返回对应的值
return node(index).item;
}
2
3
4
5
6
7
8
9
10
11
12
# set()
set需要注意的是set方法是值替换
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
checkElementIndex(index);
//找到对应index的节点
Node<E> x = node(index);
//只替换了值,没有生成新的节点
E oldVal = x.item;
x.item = element;
return oldVal;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# clear()
clear做的事就是遍历链表的每一个元素然后把每一个元素全部置空,包括 first和last
/**
* Removes all of the elements from this list.
* The list will be empty after this call returns.
*/
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
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Queue相关的方法
LinkedList实现的队列的属性本质上还是调用已经实现的链表的相关方法,源码都比较简单就不讲解了,可以尝试自己使用LinkedList来实现一个Queue或者双向队列Deque(Double-end Queue),有非常多的面试题也是类似的要求。
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
* @since 1.5
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
public E element() {
return getFirst();
}
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
* @since 1.5
*/
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
public E remove() {
return removeFirst();
}
/**
* Adds the specified element as the tail (last element) of this list.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Queue#offer})
* @since 1.5
*/
public boolean offer(E e) {
return add(e);
}
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
# Deque相关方法
/**
* Inserts the specified element at the front of this list.
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerFirst})
* @since 1.6
*/
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
/**
* Inserts the specified element at the end of this list.
*
* @param e the element to insert
* @return {@code true} (as specified by {@link Deque#offerLast})
* @since 1.6
*/
public boolean offerLast(E e) {
addLast(e);
return true;
}
/**
* Retrieves, but does not remove, the first element of this list,
* or returns {@code null} if this list is empty.
*
* @return the first element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* Retrieves, but does not remove, the last element of this list,
* or returns {@code null} if this list is empty.
*
* @return the last element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
/**
* Retrieves and removes the first element of this list,
* or returns {@code null} if this list is empty.
*
* @return the first element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
* Retrieves and removes the last element of this list,
* or returns {@code null} if this list is empty.
*
* @return the last element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
/**
* Pushes an element onto the stack represented by this list. In other
* words, inserts the element at the front of this list.
*
* <p>This method is equivalent to {@link #addFirst}.
*
* @param e the element to push
* @since 1.6
*/
public void push(E e) {
addFirst(e);
}
/**
* Pops an element from the stack represented by this list. In other
* words, removes and returns the first element of this list.
*
* <p>This method is equivalent to {@link #removeFirst()}.
*
* @return the element at the front of this list (which is the top
* of the stack represented by this list)
* @throws NoSuchElementException if this list is empty
* @since 1.6
*/
public E pop() {
return removeFirst();
}
/**
* Removes the first occurrence of the specified element in this
* list (when traversing the list from head to tail). If the list
* does not contain the element, it is unchanged.
*
* @param o element to be removed from this list, if present
* @return {@code true} if the list contained the specified element
* @since 1.6
*/
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
/**
* Removes the last occurrence of the specified element in this
* list (when traversing the list from head to tail). If the list
* does not contain the element, it is unchanged.
*
* @param o element to be removed from this list, if present
* @return {@code true} if the list contained the specified element
* @since 1.6
*/
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# 参考文献
https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/3-LinkedList.md
https://pdai.tech/md/java/collection/java-collection-LinkedList.html#queue-%E6%96%B9%E6%B3%95
https://juejin.cn/post/6844903586095169549