Iterable 与 Iterator 契约
理解 Iterable 和 Iterator 接口,以及 for-each 循环在内部的工作方式
Iterable 与 Iterator 契约 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
Iterable 与 Iterator
Iterable<T> 和 Iterator<T> 是支持 Java 增强 for 循环的接口。理解它们后,您就可以让自己的数据结构支持循环遍历。
Iterable 接口
Iterable<T> 有一个方法:iterator(),它返回一个 Iterator<T>。实现 Iterable 的任何类都可以用于增强 for 循环。
// java.lang.Iterable<T>
interface Iterable<T> {
Iterator<T> iterator();
// default: forEach, spliterator (Java 8+)
}
// Any class implementing Iterable<T> works in for-each:
class Range implements Iterable<Integer> {
private final int start, end;
Range(int start, int end) { this.start = start; this.end = end; }
public Iterator<Integer> iterator() {
return new RangeIterator(); // defined separately
}
}Iterator 接口
Iterator<T> 有三个方法:hasNext()、next() 和可选的 remove()。
// java.util.Iterator<T>
interface Iterator<T> {
boolean hasNext(); // true if more elements
T next(); // returns next element, advances cursor
default void remove() { throw new UnsupportedOperationException(); }
}
// How for-each desugars internally:
// for (T item : iterable) { use(item); }
// becomes:
Iterator<T> it = iterable.iterator();
while (it.hasNext()) {
T item = it.next();
// use(item)
}增强 For 循环的展开
增强 for 循环是 Iterable/Iterator 模式的语法糖。理解这一点有助于您正确编写自定义 Iterable。
List<String> names = List.of("Alice", "Bob", "Charlie");
// For-each (readable way)
for (String name : names) {
System.out.println(name);
}
// Equivalent explicit form
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
System.out.println(name);
}Iterator 游标状态
Iterator 会维护一个游标——也就是序列中的一个位置。调用 next() 会使游标向前移动。遍历完成后,Iterator 无法重置。
List<Integer> nums = List.of(1, 2, 3);
Iterator<Integer> it = nums.iterator();
System.out.println(it.hasNext()); // true
System.out.println(it.next()); // 1
System.out.println(it.next()); // 2
System.out.println(it.next()); // 3
System.out.println(it.hasNext()); // false
try {
it.next(); // NoSuchElementException
} catch (java.util.NoSuchElementException e) {
System.out.println("No more elements!");
}ConcurrentModificationException
使用显式 Iterator 进行遍历时,如果在遍历过程中修改集合(Iterator.remove() 除外),就会抛出 ConcurrentModificationException。
List<String> list = new ArrayList<>(List.of("a", "b", "c", "d"));
// BAD: modifying collection during for-each
try {
for (String s : list) {
if ("b".equals(s)) list.remove(s); // ConcurrentModificationException!
}
} catch (java.util.ConcurrentModificationException e) {
System.out.println("Cannot modify during iteration!");
}
// GOOD: use Iterator.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("b".equals(it.next())) it.remove(); // safe
}
System.out.println(list); // [a, c, d]多个 Iterator
每次调用 iterator() 都会返回一个全新且独立的 Iterator。同一个集合上可以同时运行多个 Iterator。
List<Integer> nums = List.of(1, 2, 3);
Iterator<Integer> a = nums.iterator();
Iterator<Integer> b = nums.iterator();
System.out.println(a.next()); // 1
System.out.println(b.next()); // 1 (independent cursor)
System.out.println(a.next()); // 2
System.out.println(b.next()); // 2Iterable 与 Iterator
关键区别:
- Iterable:能够生成 Iterator 的数据源,可以遍历多次
- Iterator:指向序列的游标,只能使用一次,并且具有状态
// Iterable: reusable
List<String> list = List.of("x", "y");
for (String s : list) {} // OK
for (String s : list) {} // OK again — new iterator each time
// Iterator: single-use
Iterator<String> it = list.iterator();
while (it.hasNext()) it.next();
// for (String s : it) {} // compile error — Iterator is not Iterable!实践:自定义文件行 Iterable
一个实现 Iterable 的文件读取器,使您可以使用增强 for 循环遍历文件中的各行。
import java.io.*;
import java.util.*;
class FileLines implements Iterable<String>, Closeable {
private final BufferedReader reader;
FileLines(String path) throws IOException {
this.reader = new BufferedReader(new FileReader(path));
}
public Iterator<String> iterator() {
return new Iterator<>() {
private String nextLine = readNext();
private String readNext() {
try { return reader.readLine(); }
catch (IOException e) { return null; }
}
public boolean hasNext() { return nextLine != null; }
public String next() {
String curr = nextLine;
nextLine = readNext();
return curr;
}
};
}
public void close() throws IOException { reader.close(); }
}forEach 默认方法
Java 8 为 Iterable 添加了默认的 forEach(Consumer) 方法。这是显式编写循环的一种便捷替代方式。
List<String> cities = List.of("New York", "London", "Tokyo");
// for-each loop
for (String city : cities) System.out.println(city);
// forEach with lambda
cities.forEach(city -> System.out.println(city));
// forEach with method reference (most concise)
cities.forEach(System.out::println);
// For transforming: use stream
cities.stream()
.map(String::toUpperCase)
.forEach(System.out::println);Iterator 模式的实际应用
以下是适合实现 Iterable/Iterator 的场景:
- 自定义数据结构(树、图、链表)
- 按需生成元素的惰性序列
- 基于资源的序列(文件行、数据库游标、消息队列)
快速检查
使用增强 for 循环遍历集合时修改集合,会抛出什么异常?
回顾:Iterable 与 Iterator 契约
要点:
- Iterable<T> 有一个方法:iterator()——支持增强 for 循环
- Iterator<T> 有 hasNext()、next() 和可选的 remove()
- 增强 for 循环是 Iterable/Iterator 模式的语法糖
- 多次调用 iterator() 可获得彼此独立的全新游标
- 切勿在增强 for 循环期间修改集合——请改用 Iterator.remove()
- 为自定义数据结构实现 Iterable,以支持增强 for 循环
常见问题解答
「Iterable 与 Iterator 契约」课时是免费的吗?
是的 — 「Iterable 与 Iterator 契约」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「Iterable 与 Iterator 契约」这节课中我会学到什么?
理解 Iterable 和 Iterator 接口,以及 for-each 循环在内部的工作方式 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「Iterable 与 Iterator 契约」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Iterable 与 Iterator 契约
- 实现自定义迭代器
- ListIterator 与双向遍历
- 快速失败与安全失败迭代器