Skip to content

Commit 6409e55

Browse files
committed
add document for Iterator and so on.
1 parent b8fcc7e commit 6409e55

File tree

7 files changed

+247
-15
lines changed

7 files changed

+247
-15
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@
66
.vscode/
77
target
88
*.iml
9-
9+
.DS_Store
10+
*.svg
11+
*.html
12+
.factorypath
13+
/docs/.asciidoctor/

README.adoc

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ TIP: 对比一下自己的实现和这些经典代码的实现,总结自己差
3838

3939
== JDK 集合类
4040

41-
*Base*::
42-
代码总行数: 103 + 604 + 469 = 1176 行,预计 2 个小时。
41+
*Base + Iterator*::
42+
代码总行数: 103 + 118 + 302 + 195 + 832 + 79 + 604 + 469 = 2702 行,预计 5 个小时。
4343
. `java.lang.Iterable`
44+
. `java.util.Iterator`
45+
. `java.util.PrimitiveIterator`
46+
. `java.util.ListIterator`
47+
. `java.util.Spliterator`
48+
. `java.util.Enumeration`
4449
. `java.util.Collection`
4550
. `java.util.AbstractCollection`
4651

@@ -75,7 +80,7 @@ TIP: 对比一下自己的实现和这些经典代码的实现,总结自己差
7580
. `java.util.BitSet`
7681

7782

78-
image::docs/java.util.Collection.png[]
83+
image::./docs/images/java.util.Collection.png[]
7984

8085
*Map*::
8186
代码总行数: 1183 + 284 + 424 + 860 + 3019 + 1339 + 812 + 1600 + 756 + 2397 + 155 + 1422 = 14251 行,预计 28 个小时。
@@ -92,17 +97,11 @@ image::docs/java.util.Collection.png[]
9297
. `java.util.Dictionary`
9398
. `java.util.Hashtable`
9499

95-
image::docs/java.util.Map.png[]
96-
97-
*Iterator*::
98-
代码总行数: 118 + 302 + 195 = 615 行,预计 2 个小时。
99-
. `java.util.Iterator`
100-
. `java.util.PrimitiveIterator`
101-
. `java.util.ListIterator`
100+
image::./docs/images/java.util.Map.png[]
102101

103102
来张总体结构图:
104103

105-
image::docs/jdk-collection-classes.png[]
104+
image::./docs/images/jdk-collection-classes.png[]
106105

107106
TIP: 这里没有包含并发相关的集合类。这块内容放到并发中一起搞。
108107

@@ -130,3 +129,7 @@ TIP: 这里没有包含并发相关的集合类。这块内容放到并发中一
130129
└── truman -- 这个目录存放相关测试代码。
131130
└── AppTest.java
132131
----
132+
133+
== 已经完成部分
134+
135+
* [x] link:./docs/java.util.Iterator.adoc[迭代器 Iterator、Spliterator 与 Iterable]
File renamed without changes.
File renamed without changes.

docs/java.util.Iterator.adoc

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
== 迭代器 Iterator、Spliterator 与 Iterable
2+
3+
=== 涉及代码
4+
5+
. `java.util.Iterator`
6+
. `java.util.PrimitiveIterator`
7+
. `java.util.ListIterator`
8+
. `java.util.Spliterator`
9+
. `java.util.Enumeration`
10+
. `java.lang.Iterable`
11+
12+
=== 迭代器模式
13+
14+
在进行代码分析之前,D瓜哥想先来讲解一下设计模式。然后结合 Java 中 `Iterator` 和 `Iteratable` ,具体分析一下迭代器在 Java 中的实现。
15+
16+
// 另外,重点看看内迭代器和外迭代器之间的不同实现。
17+
18+
[quote, Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides,《设计模式》]
19+
____
20+
迭代器模式(Iterator)::
21+
提供一种方法顺序访问一个聚合对象中各个元素,而不是暴露该对象的内部表示。
22+
____
23+
24+
类图如下:
25+
26+
[plantuml, iterator-class-diagram, format=svg,align="center",width=100%]
27+
....
28+
@startuml
29+
'skinparam nodesep 70
30+
31+
title <b>迭代器模式</b>
32+
33+
abstract class Iterator {
34+
+ {abstract} first() :Object
35+
+ {abstract} next() :Object
36+
+ {abstract} isDone() :boolean
37+
+ {abstract} currentItem() :Object
38+
}
39+
note top: 迭代抽象类,用于定义得到开始对象、\n得到下一个对象、判断是否到结尾、\n当前对象等抽象方法,统一接口。
40+
41+
class ConcreteIterator {
42+
}
43+
note bottom: 具体迭代器类,继承 Iterator,\n实现开始、下一个、是否结尾、\n当前对象等方法。
44+
45+
abstract class Aggregate {
46+
+ {abstract} createIterator() :Iterator
47+
}
48+
note top: 聚集抽象类
49+
50+
class ConcreteAggregate {
51+
+ createIterator() :Iterator
52+
}
53+
note bottom: 具体聚集类,继承 Aggregate。
54+
55+
class Client {
56+
}
57+
58+
Client -left-> Aggregate
59+
Client -right-> Iterator
60+
61+
Aggregate <|-- ConcreteAggregate
62+
Iterator <|-- ConcreteIterator
63+
64+
ConcreteIterator -left-> ConcreteAggregate
65+
ConcreteIterator <.. ConcreteAggregate
66+
67+
@enduml
68+
....
69+
70+
71+
当需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式。
72+
73+
当需要对聚集有多种方式遍历时,可以考虑用迭代器模式。
74+
75+
为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。
76+
77+
//像IEnumerable接口也是为迭代器模式而准备的。不管如何,学习一下GoF的迭代器模式的基本结构,还是很有学习价值的。研究历史是为了更好地迎接未来。
78+
79+
尽管我们不需要显式的引用迭代器,但系统本身还是通过迭代器来实现遍历的。总地来说,迭代器(`Iterator`)模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
80+
81+
请问: Java 中是如何应用迭代器模式呢?
82+
83+
=== `Iterator`
84+
85+
从上面的设计模式可以看出,迭代器模式就是为了遍历不同的聚集结构提供诸如开始、下一个、是否结束、当前元素等常见操作的统一接口。来看看 Java 集合类是如何提炼接口的。
86+
87+
.java.util.Iterator
88+
[source,java]
89+
----
90+
public interface Iterator<E> {
91+
92+
boolean hasNext();
93+
94+
E next();
95+
96+
default void remove() {
97+
throw new UnsupportedOperationException("remove");
98+
}
99+
100+
/**
101+
* @since 1.8
102+
*/
103+
default void forEachRemaining(Consumer<? super E> action) {
104+
Objects.requireNonNull(action);
105+
while (hasNext())
106+
action.accept(next());
107+
}
108+
}
109+
----
110+
111+
从上述代码中,可以看出 Java 提取了 `boolean hasNext()`、 `E next()`、 `void remove()` 等三个操作方法;在 Java 8 中,为了支持 Stream API,有增加了 `void forEachRemaining(Consumer<? super E> action)` 方法。
112+
113+
这里多扯一句,Java 在 1.2 以前迭代器是通过另外一个接口实现的:
114+
115+
.java.util.Enumeration
116+
[source,java]
117+
----
118+
public interface Enumeration<E> {
119+
120+
boolean hasMoreElements();
121+
122+
E nextElement();
123+
}
124+
----
125+
126+
与上面的 `java.util.Iterator` 对比可以看出,两者差别不大。那为什么 Java 在已有 `java.util.Iterator` 接口的情况下,还要推出 `java.util.Enumeration` 接口呢?在 `java.util.Iterator` 接口的 JavaDoc 中给出了如下理由:
127+
128+
* Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
129+
* Method names have been improved.
130+
131+
我们都知道,在 Java 8 之前,接口中的方法不能有任何实现。所以,为了保持兼容性,不能在已有接口中增加方法。只能另起炉灶,把“洞”补上。这也就不难理解,为什么又搞出了个 `java.util.Iterator`。
132+
133+
这里再多提一句,需要增加自定义的迭代器实现时,请优先选择 `java.util.Iterator`。
134+
135+
请问:既然有迭代器接口定义了,那么 Java 又是如何生成迭代器实例呢?
136+
137+
=== `Iterable`
138+
139+
既然迭代器可以抽象成一个公共的接口,那么生成迭代器实例的这个操作,也可以抽象成一个接口。 Java 也确实是这样做的:
140+
141+
.java.lang.Iterable
142+
[source,java]
143+
----
144+
public interface Iterable<T> {
145+
146+
Iterator<T> iterator();
147+
148+
/**
149+
* @since 1.8
150+
*/
151+
default void forEach(Consumer<? super T> action) {
152+
Objects.requireNonNull(action);
153+
for (T t : this) {
154+
action.accept(t);
155+
}
156+
}
157+
158+
/**
159+
* @since 1.8
160+
*/
161+
default Spliterator<T> spliterator() {
162+
return Spliterators.spliteratorUnknownSize(iterator(), 0);
163+
}
164+
}
165+
----
166+
167+
从类的定义中,可以看到 `java.lang.Iterable` 提供了 `iterator()`,用于创建 `java.util.Iterator` 示例对象。
168+
169+
在 Java 8 中,为了支持 Lambda 表达式和 Stream API,又增加了 `forEach(Consumer<? super T> action)` 和 `spliterator()` 方法。
170+
171+
在思考实现原理的过程中,D瓜哥突然想到,`java.lang.Iterable` 就是一个工厂方法模式的应用。来分析一下:
172+
173+
=== 工厂方法模式
174+
175+
先来看看工厂方法模式的定义:
176+
177+
[quote, Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides,《设计模式》]
178+
____
179+
工厂方法模式(Factory Method)::
180+
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
181+
____
182+
183+
类图如下:
184+
185+
[plantuml, factory-method-class-diagram, format=svg,align="center",width=100%]
186+
....
187+
@startuml
188+
title <b>工厂方法模式</b>
189+
190+
abstract class Product {
191+
}
192+
note top: 定义工厂方法所创建的对象的接口。
193+
194+
class ConcreteProduct {
195+
}
196+
note bottom: 具体的产品,实现了 Product 接口。
197+
198+
abstract class Factory {
199+
+ {abstract} factoryMethod() :Product
200+
}
201+
note top: 声明工厂方法, 该方法返回一个 Product 类型的对象。
202+
203+
class ConcreteFactory {
204+
}
205+
note bottom: 重定义工厂方法以返回一个 ConcreteProduct 实例。
206+
207+
Product <|-- ConcreteProduct
208+
Factory <|-- ConcreteFactory
209+
ConcreteFactory -right-> ConcreteProduct
210+
211+
@enduml
212+
....
213+
214+
* `java.lang.Iterable` 就相当于 `Factory` 接口,也就是工厂;
215+
* `java.util.Iterator` 就相当于工厂生成的产品 `Product`;
216+
* `iterator()` 方法就是工厂方法 `factoryMethod()`;
217+
* `java.lang.Iterable` 和 `java.util.Iterator` 子类,都放在了各个集合类中来具体实现。
218+
219+
在各个聚集类中,去实现 `java.lang.Iterable` 接口,然后根据聚集类的情况,返回对应的 `java.util.Iterator` 具体类对象即可。
220+
221+
细心的童鞋,可能发现还有个类似迭代器的类 `Spliterator`。这是个什么类?为啥要增加相关的接口呢?
222+
223+
=== `Spliterator`
224+
225+
226+

pom.xml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<groupId>com.diguage</groupId>
99
<artifactId>truman</artifactId>
1010
<version>0.0.1-SNAPSHOT</version>
11-
1211
<name>JDK Source Analysis</name>
13-
<!-- <url>http://www.example.com</url>-->
12+
<description>truman 这个名来源于《楚门的世界 The Truman Show》,取义走过 API 的墙,看到"外面的"真实的世界。</description>
13+
<url>https://www.diguage.com/</url>
1414

1515
<properties>
1616
<jmh.version>1.21</jmh.version>
@@ -40,7 +40,6 @@
4040
<version>${junit.version}</version>
4141
<scope>test</scope>
4242
</dependency>
43-
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
4443
<dependency>
4544
<groupId>org.assertj</groupId>
4645
<artifactId>assertj-core</artifactId>

0 commit comments

Comments
 (0)