以脑图的形式来展示Java集合知识,让零碎知识点形成体系
Iterator(迭代器)是一种设计模式,是一个对象,用于遍历集合中的所有元素。
Iterator 包含四个方法,分别是:next()、hasNext()、remove()、forEachRemaining(Consumer<? super E> action)
Collection 接口继承 java.lang.Iterable,因此所有 Collection 实现类都拥有 Iterator 迭代能力。
逆向思考,Iterable 面向众多的 Collection 类型实现类,定义的方法就不可能太定制化,因此 Iterator 定义的功能比较简单。
仅有如上所列出来的四种方法,并且该迭代器只能够单向移动。
由于 List 类型的 Collection 是一个有序集合,对于拥有双向迭代是很有意义的。
ListIterator 接口则在继承 Iterator 接口的基础上定义了:add(E newElement)、set(E newElement)、hasPrevious()、previous()、nextIndex()、previousIndex() 等方法,使得 ListIterator 迭代能力增强,能够进行双向迭代、迭代过程中可以进行增删改操作。
1 List<String> list = new LinkedList<>();
2 list.add("aaa");
3 list.add("bbb");
4 list.add("ccc");
5
6 ListIterator<String> listIterator = list.listIterator();
7
8 //迭代器位置: add-1 | aaa bbb ccc
9 listIterator.add("add-1");
10 // add-1 add-1 | aaa bbb ccc
11 listIterator.add("add-2");
12
13 // 返回: aaa
14 // add-1 add-1 aaa | bbb ccc
15 listIterator.next();
16 // add-1 add-1 aaa-set | bbb ccc
17 listIterator.set("aaa-set");
18 // bbb
19 // add-1 add-1 aaa-set bbb | ccc
20 listIterator.next();
21
22 // 返回: bbb
23 // add-1 add-1 aaa-set | bbb ccc
24 listIterator.previous();
25 // add-1 add-1 aaa-set | bbb-set ccc
26 listIterator.set("bbb-set");
27
28 // 删除 bbb-set
29 listIterator.remove();
30 listIterator.remove();
31
32 System.out.println(list);
很多书本都有给出这样子的结论:
迭代同时修改问题:
一个迭代器指向另一个迭代器刚刚删除的元素,则现在这个迭代器就变成无效的了(节点删除被回收;即使没被回收,该节点的前后引用也被重置为null)。
链表迭代器有能够检测到这种修改的功能,当发现集合被修改了,将会抛出一个 ConcurrentModificationException 异常
为什么出现上面的这些现象与问题呢,我们还是从源码中寻找答案吧
有多个集合类根据自己的特点实现了 ListIterator 接口,其实现都大同小异,这里我们主要分析 LinkedList 中所实现的 ListIterator。
首先我们来分析 LinkedList 的 listIterator() 和 listIterator(int index) 方法获取 ListIterator 迭代器过程。
1 // AbstractList.java
2 // listIterator() 方法 LinkedList 类本身并没有重写,需要追溯到 AbstractList 抽象类
3
4 // 获取 ListIterator 迭代器
5 public ListIterator<E> listIterator() {
6 return listIterator(0);
7 }
8
9 public ListIterator<E> listIterator(final int index) {
10 rangeCheckForAdd(index); // 检查 index 范围是否超出
11
12 return new ListItr(index); // 该抽象类也有实现 ListItr 类
13 }
14
15 private void rangeCheckForAdd(int index) {
16 if (index < 0 || index > size())
17 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
18 }
1 // LinkedList.java
2 // LinkedList 类重写了 listIterator(int index) 方法
3
4 public ListIterator<E> listIterator(int index) {
5 checkPositionIndex(index); // 同理 检查 index 范围;相关代码就不贴了
6 return new ListItr(index);
7 }
8
9
10 private class ListItr implements ListIterator<E> {
11 private Node<E> lastReturned; // 上一次处理的节点
12 private Node<E> next; // 即将要处理的节点
13 private int nextIndex; // 即将要处理的节点的 index
14 // modCount 表示集合和迭代器修改的次数;expectedModCount 表示当前迭代器对集合修改的次数
15 private int expectedModCount = modCount;
16
17 ListItr(int index) {
18 // assert isPositionIndex(index);
19 next = (index == size) ? null : node(index);
20 nextIndex = index;
21 }
22
23 public boolean hasNext() {
24 return nextIndex < size;
25 }
26
27 /**
28 * 处理对象:迭代器当前的 next 节点
29 * 将处理目标储到 lastReturned 变量中
30 * 然后将当前的 next.next 节点保存起来,用于下一次迭代处理
31 * nextIndex 同时 +1
32 * 返回 lastReturned.item 元素
33 * 执行后:lastReturned 指向该次处理的节点;next、nextIndex 指向该次处理节点的后一个节点
34 */
35 public E next() {
36 // 检查 modCount 与 expectedModCount 是否相等
37 // 实际检查该链表是否被其他迭代器或者集合本身修改
38 checkForComodification();
39 // 判断是否存在 next 节点
40 if (!hasNext())
41 throw new NoSuchElementException();
42
43 lastReturned = next; // 将这次返回的 node 节点更新到迭代器中的 lastReturned 变量
44 next = next.next; // 将下一次需要处理 node 节点更新会 next 变量
45 nextIndex++; // 变量 nextIndex +1
46 return lastReturned.item; // 返回元素
47 }
48
49 public boolean hasPrevious() {
50 return nextIndex > 0;
51 }
52
53 /**
54 * 处理对象:迭代器当前的 next.prev 节点
55 * 将处理目标储到 lastReturned 变量中
56 * 然后将当前的 next.prev 节点保存起来,用于下一次迭代处理
57 * nextIndex 同时 -1
58 * 返回当前的 next.item 元素
59 * 执行后:next、lastReturned、nextIndex 指向该次处理节点的前一个节点
60 */
61 public E previous() {
62 checkForComodification();
63 // 判断是否存在 prev 节点
64 if (!hasPrevious())
65 throw new NoSuchElementException();
66
67 // 处理当前 next 的 prev 节点
68 // 特殊情况:next = null 时,则它的 prev 节点为 last 节点
69 lastReturned = next = (next == null) ? last : next.prev;
70 nextIndex--; // nextIndex -1
71 return lastReturned.item;
72 }
73
74 public int nextIndex() {
75 return nextIndex;
76 }
77
78 public int previousIndex() {
79 return nextIndex - 1;
80 }
81
82 /**
83 * 处理对象:lastReturned
84 * 删除 lastReturned 指向的节点,并置为 null
85 * 同时保证 next 和 nextIndex 指向同一个节点
86 */
87 public void remove() {
88 checkForComodification(); // 同理, 检查 modCount 与 expectedModCount 是否相等
89 if (lastReturned == null)
90 throw new IllegalStateException();
91
92 Node<E> lastNext = lastReturned.next; // 暂存 lastReturned 的 next 节点,用于恢复迭代状态
93 unlink(lastReturned); // 删除最后返回的节点 modCount++;
94
95 // 分迭代方向处理(因为删除一个节点后,需要恢复迭代状态:next 和 nextIndex 指向同一个节点)
96 if (next == lastReturned) // next 与 lastReturned 节点相同则表明最近一次迭代操作是 previous()
97 next = lastNext; // 删除了原有 next 指向的节点,因此 nextIndex 相对指向的节点变为 next.next,需要更新 next 变量的指向
98 else
99 nextIndex--; // next() 迭代方向;删除了next前面的节点,因此next的相对位置发生变化,需要 nextIndex -1
100 lastReturned = null;
101 expectedModCount++; // 同时 expectedModCount++
102 }
103
104 /**
105 * 处理对象:lastReturned
106 */
107 public void set(E e) {
108 if (lastReturned == null)
109 throw new IllegalStateException();
110 checkForComodification();
111 lastReturned.item = e;
112 }
113
114 /**
115 * 分位置进行添加
116 */
117 public void add(E e) {
118 checkForComodification();
119 lastReturned = null;
120 if (next == null)
121 linkLast(e);
122 else
123 linkBefore(e, next);
124 nextIndex++;
125 expectedModCount++;
126 }
127
128 public void forEachRemaining(Consumer<? super E> action) {
129 Objects.requireNonNull(action);
130 while (modCount == expectedModCount && nextIndex < size) {
131 action.accept(next.item);
132 lastReturned = next;
133 next = next.next;
134 nextIndex++;
135 }
136 checkForComodification();
137 }
138
139 /**
140 * 检查 modCount 与 expectedModCount 是否相等,否则抛出错误
141 * ListIterator 迭代器进行增删操作时,都会同时对这两个变量 +1
142 * 目的:
143 * 使用 ListIterator 迭代器期间,LinkedList 对象有且只能当前这一个迭代器可以进行修改
144 * 避免 LinkedList 对象本身以及其他迭代器进行修改导致链表混乱
145 */
146 final void checkForComodification() {
147 if (modCount != expectedModCount)
148 throw new ConcurrentModificationException();
149 }
150 }
总的来说 ListIterator 是记录 List 位置的一个对象,它主要的成员变量是 lastReturned、next、nextIndex 以及 expectedModCount。
这就很好地解释上面所提到的一些现象与问题了。
典型的就是连续两个 remove() 会报错,那是因为第一个 reomve() 之后 lastReturned 被置为null;第二个 remove() 处理的对象是null,因此炮锤 IllegalStateException
在 github 上建了一个 repository ,Java Core Knowledge Tree,各位看官若是喜欢请给个star,以示鼓励,谢谢。
https://github.com/suifeng412/JCKTree
(以上是自己的一些见解,若有不足或者错误的地方请各位指出)
作者:那一叶随风 http://www.cnblogs.com/phpstudy2015-6/
原文地址: https://cloud.tencent.com/developer/article/1414559
声明:本博客文章为原创,只代表本人在工作学习中某一时间内总结的观点或结论。转载时请在文章页面明显位置给出原文链接