目录
  1. 1. LinkedList源码解析
    1. 1.1. 一、LinkedList数据结构
    2. 1.2. 二、LinkedList类定义
      1. 1.2.1. 1.为什么要继承AbstractSequentialList
      2. 1.2.2. 2.LinkedList的类图关系
      3. 1.2.3. 3.不同步
    3. 1.3. 三、LinkedList属性
    4. 1.4. 四、LinkedList构造函数
    5. 1.5. 五、核心函数分析
      1. 1.5.1. 1.添加元素
        1. 1.5.1.1. 1.1addFirst
        2. 1.5.1.2. 1.2addLast
        3. 1.5.1.3. 1.3add
        4. 1.5.1.4. 1.4addAll
          1. 1.5.1.4.1. 1.4.1对addAll函数的思考
      2. 1.5.2. 2.删除元素
        1. 1.5.2.1. 2.1removeFirst
        2. 1.5.2.2. 2.2removeLast
        3. 1.5.2.3. 2.3remove
        4. 1.5.2.4. 2.4clear
      3. 1.5.3. 3.修改元素
      4. 1.5.4. 4.查找元素
        1. 1.5.4.1. 4.1getFirst
        2. 1.5.4.2. 4.2getLast
        3. 1.5.4.3. 4.3get
        4. 1.5.4.4. 4.4contains
        5. 1.5.4.5. 4.5indexOf
        6. 1.5.4.6. 4.6lastIndexOf
      5. 1.5.5. 5.node随机访问函数
      6. 1.5.6. 6.其他public方法
      7. 1.5.7. 7.Queue队列操作
        1. 1.5.7.1. 7.1peek
        2. 1.5.7.2. 7.2element
        3. 1.5.7.3. 7.3poll
        4. 1.5.7.4. 7.4remove
        5. 1.5.7.5. 7.5offer
      8. 1.5.8. 8.Deque双端队列操作
      9. 1.5.9. 9.栈操作
    6. 1.6. 六、LinkedList的遍历
      1. 1.6.1. 1.迭代器Iterator遍历
      2. 1.6.2. 2.迭代器ListIterator遍历
      3. 1.6.3. 3.foreach循环遍历
    7. 1.7. 七、LinkedList内部类
      1. 1.7.1. 1.ListItr内部类
      2. 1.7.2. 2.Node内部类
      3. 1.7.3. 3.DescendingIterator降序迭代器
      4. 1.7.4. 4.LLSpliterator分割器
LinkedList源码解析

LinkedList源码解析

一、LinkedList数据结构

LinkList数据结构.png
说明:如上如所示,LinkedList底层使用的双向链表结构,有一个头节点和尾节点,双向链意味着可以从头开始正向遍历,或者从尾开始逆向遍历,并且可以针对头部和尾部进行相应的操作
  既然是双向链表,那么必定存在一种数据结构—我们称之为节点,节点实例保存业务数据,前一节点的位置信息和后一节点的位置信息
LinkList节点信息.png

二、LinkedList类定义

1
2
3
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • LinkedList是一个继承于AbstractSequentialList的双向链表,它也可以被当做堆栈、队列或双端队列进行操作
  • 实现List接口,能对它进行队列操作,并允许包括null值
  • 实现Deque接口,即能将LinkedList当作双端队列使用
  • 实现了Cloneable接口,即覆盖了函数clone(),能克隆
  • 实现了java.io.Serializable接口,意味着LinkedList支持序列化,能通过序列化去传输
  • LinkedList是非同步的

1.为什么要继承AbstractSequentialList

  AbstractSequentialList实现了get(int index)、set(int index,E element)、add(int index,E element)和remove(int index)这些骨干性函数,降低了List的接口的复杂度,这些接口都是随机访问List的,既然它继承于AbstractSequentialList就相当于已经实现”get(int index)这些接口”
  若需要通过AbstractSequentialList自己实现一个列表:只需要扩展此类,并提供listIterator()和size()方法的实现即可
  若要实现不可修改的列表:则需要实现列表迭代器的hasNext、next、hasPrevious、previous和index方法即可

2.LinkedList的类图关系

LinkedList的类图关系.png

3.不同步

  LinkedList实现不是同步的,如果多个线程同时访问一个LinkedList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步
  这通常是通过同步那些用来封装列表对象实现的.但是如果没有这样的对象,则该列表需要运用(Collections.synchronizedList)来进行”包装”,该方法最好是在创建列表对象时完成,为了避免对列表进行突发的非同步操作

1
List list = Collections.synchronizedList(new LinkedList(...));

  类中的iterator()方法和listIterator()方法返回的iterators迭代器是fail-fast的:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件

三、LinkedList属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 容量
transient int size = 0;

/**
* 首节点
* 不变的情况: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;

/**
* 尾节点
* 不变的情况: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;

  LinkedList的属性非常简单,一个头节点、一个尾节点、一个表示链表中实际元素个数的变量.

注意:头节点、尾节点都有transient关键字修饰,这意味着序列化时该属性是不会序列化的

四、LinkedList构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 默认构造
*/
public LinkedList() {
}

/**
* 通过一个集合初始化LinkedList,元素的顺序由集合的迭代器返回顺序决定
* @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);
}

注意:此处默认构造会形成空链表,即头节点的前一节点和后一节点均为null
在之前版本中则是将头节点的prev和next全部指向first实例,这样整个链表只有first一个节点,表示空的链表,执行完构造函数后first实例自身形成一个闭环,如图
构造空链表.png

五、核心函数分析

1.添加元素

LinkedList提供了头插入addFirst、尾插入addLast、add、addAll等方法

1.1addFirst

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
/**
* 添加元素作为第一个元素
* @param e the element to add
*/
@Override
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;
// 如果之前的首节点为空(即size==0),则尾节点与首节点为同一个
// 如果之前的首节点不为空,将之前的首节点的向前指向链接到新的首节点上
if (f == null) {
last = newNode;
} else {
f.prev = newNode;
}
// 长度+1
size++;
// 修改次数 + 1
modCount++;
}

1.2addLast

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
/**
* 添加元素作为最后一个元素
* @param e the element to add
*/
@Override
public void addLast(E e) {
linkLast(e);
}
/**
* 链接一个元素作为尾节点
*/
void linkLast(E e) {
// 得到尾节点
final Node<E> l = last;
// 新建节点元素
final Node<E> newNode = new Node<>(l, e, null);
// 设置新的尾节点
last = newNode;
// 如果之前的尾节点为空(即size==0),则尾节点与首节点为同一个
// 如果之前的尾节点不为空,将之前的尾节点的向后指向链接到新的尾节点上
if (l == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
modCount++;
}

1.3add

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
/**
* 添加一个元素,默认添加到末尾作为最后一个元素
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
@Override
public boolean add(E e) {
linkLast(e);
return true;
}

/**
* 列表的指定位置插入指定的元素
* - 当前位置如果有元素,则向右移动元素(对应索引加1),由linkBefore体现
* @param index 待插入元素的索引
* @param element 待插入元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public void add(int index, E element) {
checkPositionIndex(index);
// 如果指定位置为最后,则添加到链表最后
// 如果指定位置不是最后,则添加到指定元素前
if (index == size) {
linkLast(element);
} else {
linkBefore(element, node(index));
}
}

/**
* 在指定节点前插入节点,节点succ不能为空
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 获取前一个节点
final Node<E> pred = succ.prev;
// 创建一个新的节点,向前指向pred(前一个节点),向后指向succ(当前节点)
final Node<E> newNode = new Node<>(pred, e, succ);
// 在pred.next 和 succ.prev之间链接上新的节点
// 如果succ当前节点的前一个节点为空,则新节点为首节点
succ.prev = newNode;
if (pred == null) {
first = newNode;
} else {
pred.next = newNode;
}
size++;
modCount++;
}

示例:

1
2
3
List<Integer> lists = new LinkedList<Integer>();
lists.add(5);
lists.add(6);

add.png

1.4addAll

  addAll有两个重载函数,addAll(Collection<? extends E>)型和addAll(int, Collection<? extends E>)型,我们平时习惯调用的addAll(Collection<? extends E>)型会转化为addAll(int, Collection<? extends 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
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
/**
* 从指定的位置插入指定集合的所有元素
* - 如果当前位置有元素存在,将该位置的元素移动到右边
* - 新的元素按照指定集合的迭代器顺序放置
* @param index 插入第一个元素的索引
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
@Override
public boolean addAll(int index, Collection<? extends E> c) {
// 检查索引是否正确 (0<=index<=size)
checkPositionIndex(index);
// 得到元素数组
Object[] a = c.toArray();
// 新添加的元素个数
int numNew = a.length;
// 若没有元素添加则直接返回false
if (numNew == 0) {
return false;
}
// 记录要插入的左右节点信息
Node<E> pred, succ;
/*
* 如果插入位置为链表末尾
* - 让当前元素前一节点链接原有的尾节点,后一节点链接null
* 否则查找出指定位置的节点,在这节点之前链接上元素
* - 前一节点为旧节点的前一节点,后一节点为旧节点
*/
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}

// 遍历数组
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
// 生成新节点元素
Node<E> newNode = new Node<>(pred, e, null);
// 插入位置的前一节点为null,新节点设置为首节点
// 插入位置的前一节点不为null,前一节点的下一节点设置为新节点
if (pred == null) {
first = newNode;
} else {
pred.next = newNode;
}
// pred设置为新节点,即插入位置后移
pred = newNode;
}

// 如果succ为null,即一直在最后位置添加,让前一个节点成为最后节点
// 如果succ不为null,已经没有元素可以链接,则将pred和succ闭合起来
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}

示例:

1
2
3
List<Integer> lists = new LinkedList<Integer>();
lists.add(5);
lists.addAll(0, Arrays.asList(2, 3, 4, 5));

上述代码内部的链表结构如下:
addAll.png

1.4.1对addAll函数的思考

  在addAll函数中,传入一个集合参数和插入位置,然后将集合转化为数组,然后在遍历数组,挨个添加数组元素,为什么要先转化为数组再进行遍历,而不是直接遍历集合?
从效果上两者是完全等价的,都可以达到遍历的效果

  1. 如果直接遍历集合的话,那么在遍历过程中需要插入元素,在堆上分配内存空间,修改指针域,这个过程就会一直占用着这个集合,考虑正确同步的话其他线程只能一直等到
  2. 如果转化为数组,只需要遍历集合,而遍历集合过程中不需要额外的操作,所以占用时间相对是较短的,这样就利于其他线程尽快的使用这个集合,即有利于提高多线程访问该集合的效率,尽可能短时间的阻塞

2.删除元素

LinkedList提供了头删除removeFirst、了尾删除removeLast、remove、clear等删除方法

2.1removeFirst

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
/**
* 删除第一个元素并返回删除的元素
* @return the first element from this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E removeFirst() {
// 获取到首节点,判断首节点为空则抛出异常
final Node<E> f = first;
if (f == null) {
throw new NoSuchElementException();
}
return unlinkFirst(f);
}
/**
* 内部方法:删除首节点并返回删除的前首节点的值
*/
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
// 获取到首节点的值
final E element = f.item;
// 得到下一个节点
final Node<E> next = f.next;
// 便于垃圾回收清理
f.item = null;
f.next = null; // help GC
// 下一节点设置为首节点
first = next;
// 如果不存在下一个节点:则首尾都为null(空表)
// 如果存在下一节点:它的向前指向为null,
if (next == null) {
last = null;
} else {
next.prev = null;
}
size--;
modCount++;
return element;
}

2.2removeLast

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
/**
* 删除最后一个元素并返回删除的值
* @return the last element from this list
* @throws NoSuchElementException if this list is empty
*/
@Override
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;
// 得出此尾节点的前一节点
final Node<E> prev = l.prev;
// 清空尾节点的值和 前一节点(隔断/孤立尾节点) 便于垃圾回收清理
l.item = null;
l.prev = null; // help GC
// 前一节点设置为尾节点
last = prev;
// 如果不存在前一个节点:则首尾都为null(空表)
// 如果存在前一个节点:则它的向后指向为null
if (prev == null) {
first = null;
} else {
prev.next = null;
}
size--;
modCount++;
return element;
}

2.3remove

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
* 出队(从前端)
* - 如果不存在会抛出异常而不是返回null,存在的话会返回值并移除这个元素(节点)
* @return the head of this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E remove() {
return removeFirst();
}
/**
* 删除列表指定位置的元素
* - 向左移动剩余子元素(对应索引减1)
* - 返回列表上被移除的值
* @param index 被移除元素的索引
* @return 指定位置之前的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
/**
* 删除指定节点并返回被删除的元素值
*/
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;

// 如果前一节点为null,即当前节点为首节点,让下一节点成为首节点
// 如果前一节点不为null,断开当前节点的前一节点链接(下方断开后一节点链接)
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}

// 如果后一节点为null,即当前节点为尾节点,让前一节点成为尾节点
// 如果后一节点不为null,断开当前节点的后一节点链接
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}

// 方便gc回收,当前的值置为null
x.item = null;
size--;
modCount++;
return element;
}
/**
* 删除指定元素
* - 从first节点开始第一个出现的元素
* - 如果列表中不包含此元素,就不改变
* @param o element to be removed from this list, if present
* @return {@code true} if this list contained the specified element
*/
@Override
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;
}

2.4clear

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 清空表:方便gc回收
*/
@Override
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++;
}

3.修改元素

LinkedList提供了set(int index, E element)方法来修改指定索引上的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 用指定位置的元素替换指定位置的元素
* @param index 代替指定位置的元素
* @param element 指定位置被存储的元素
* @return 指定位置之前的元素值
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}

4.查找元素

LinkedList提供了getFirst、getLast、get、contains、indexOf、lastIndexOf这些查找元素方法

4.1getFirst

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 获取第一个元素
* @return the first element in this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E getFirst() {
// 获取到首节点,判断首节点为空则抛出异常
final Node<E> f = first;
if (f == null) {
throw new NoSuchElementException();
}
return f.item;
}

4.2getLast

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 获取最后一个元素
* @return the last element in this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E getLast() {
// 获取尾节点,如果为空则抛出异常
final Node<E> l = last;
if (l == null) {
throw new NoSuchElementException();
}
return l.item;
}

4.3get

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 获取指定索引的节点值
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
@Override
public E get(int index) {
// 检查元素索引:0<=index<=size
checkElementIndex(index);
return node(index).item;
}

4.4contains

1
2
3
4
5
6
7
8
9
/**
* 检查是否包含某个元素
* @param o element whose presence in this list is to be tested
* @return {@code true} if this list contains the specified element
*/
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}

4.5indexOf

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
/**
* 获取指定元素第一次出现的索引,不存在就返回-1
* - 不能二分查找了,所以通常根据索引获得元素的速度比下方通过元素获得索引的速度快
* @param o 待索引的元素
* @return
*/
@Override
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
return index;
}
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
return index;
}
index++;
}
}
return -1;
}

4.6lastIndexOf

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
/**
* 获取指定元素从last开始第一次出现的索引,不存在就返回-1
* @param o 待索引的元素
* @return
*/
@Override
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null) {
return index;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item)) {
return index;
}
}
}
return -1;
}

5.node随机访问函数

  由LinkedList的类结构可以看出,LinkedList是AbstractSequentialList的子类.AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element)和remove(int index)这些随机访问的函数,那么LinkedList也实现了这些随机访问的接口.LinkedList具体是如何实现随机访问的?即具体是如何定义index这个参数的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 返回指定索引的节点
*/
Node<E> node(int index) {
// assert isElementIndex(index);
// 先来一次二分,减少遍历查找的次数
if (index < (size >> 1)) {
// index==0时不会循环,直接返回first 下方同理
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;
}
}

  该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中:我们看到这里有一个加速动作.源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果 index>size/2,就只从位置size往前遍历到位置index处.这样可以减少一部分不必要的遍历

6.其他public方法

clone、toArray、toArray(T[] a)
以及支持序列化写入函数writeObject(java.io.ObjectOutputStream s)和读取函数readObject(java.io.ObjectInputStream s)
—具体见源码全解析

7.Queue队列操作

Queue操作提供了peek、element、poll、remove、offer这些方法

7.1peek

1
2
3
4
5
6
7
8
9
/**
* 出队(从前端)获得第一个元素,不存在返回null,不会删除元素(节点)
* @return 头部元素没有则返回null
*/
@Override
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}

7.2element

1
2
3
4
5
6
7
8
9
/**
* 出队(从前端)获取第一个元素,若为null抛出异常而不是返回null
* @return the head of this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E element() {
return getFirst();
}

7.3poll

1
2
3
4
5
6
7
8
9
10
/**
* 出队(从前端)
* - 如果不存在返回null,存在的话会返回值并删除这个元素(节点)
* @return
*/
@Override
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}

7.4remove

1
2
3
4
5
6
7
8
9
10
/**
* 出队(从前端)
* - 如果不存在会抛出异常而不是返回null,存在的话会返回值并移除这个元素(节点)
* @return the head of this list
* @throws NoSuchElementException if this list is empty
*/
@Override
public E remove() {
return removeFirst();
}

7.5offer

1
2
3
4
5
6
7
8
9
10
/**
* 入队(从后端)
* - 始终返回true
* @param e the element to add
* @return
*/
@Override
public boolean offer(E e) {
return add(e);
}

8.Deque双端队列操作

Deque操作提供了offerFirst(E e)、offerLast(E e)、peekFirst()、peekLast()、pollFirst()、pollLast()

9.栈操作

push(E e)、pop()、removeFirstOccurrence、removeLastOccurrence(Object o)这些方法

六、LinkedList的遍历

1.迭代器Iterator遍历

1
2
3
4
Iterator iter = list.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}

2.迭代器ListIterator遍历

1
2
3
4
5
6
7
8
9
ListIterator<String> lIter = list.listIterator();
//顺向遍历
while(lIter.hasNext()) {
  System.out.println(lIter.next());
}
//逆向遍历
while(lIter.hasPrevious()) {
  System.out.println(lIter.previous());
}

3.foreach循环遍历

1
2
3
for(String str:list) {
System.out.println(str);
}

七、LinkedList内部类

1.ListItr内部类

列表迭代器内部实现了hasNext、next、hasPrevious、previous、nextIndex、previousIndex、remove、set、add、forEachRemaining、checkForComodification等方法

2.Node内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}
}

内部类Node就是实际的节点,用于存放实际元素的地方

3.DescendingIterator降序迭代器

实现了hasNext、next、remove等方法

4.LLSpliterator分割器

文章作者: Eric Liang
文章链接: https://ericql.github.io/2019/11/12/01-Java%E5%9F%BA%E7%A1%80%E7%AF%87/03-%E9%9B%86%E5%90%88%E7%AF%87/LinkedList%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Eric Liang
打赏
  • 微信
  • 支付宝