跳到主要内容

迭代器模式

问题

什么是迭代器模式?Java 的 Iterator 和 Iterable 有什么区别?

答案

核心思想

提供一种顺序访问集合元素的方法,而不暴露集合的内部结构。客户端通过统一的 Iterator 接口遍历不同的集合类型。

Java 迭代器体系

Iterator 和 Iterable
// Iterable:可迭代的(集合实现此接口)
public interface Iterable<T> {
Iterator<T> iterator(); // 返回迭代器
}

// Iterator:迭代器(遍历的工具)
public interface Iterator<E> {
boolean hasNext(); // 是否还有下一个
E next(); // 获取下一个
void remove(); // 删除当前元素
}

实现了 Iterable 的类可以使用 for-each 循环:

List<String> list = List.of("a", "b", "c");

// for-each 编译后就是 Iterator 调用
for (String s : list) {
System.out.println(s);
}

// 等价于
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

自定义迭代器

自定义可迭代集合
public class NumberRange implements Iterable<Integer> {
private final int start;
private final int end;

public NumberRange(int start, int end) {
this.start = start;
this.end = end;
}

@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
private int current = start;

@Override
public boolean hasNext() { return current <= end; }

@Override
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return current++;
}
};
}
}

// 使用 for-each
for (int n : new NumberRange(1, 5)) {
System.out.println(n); // 1, 2, 3, 4, 5
}

fail-fast 机制

在遍历过程中修改集合会抛出 ConcurrentModificationException

fail-fast 示例
List<String> list = new ArrayList<>(List.of("a", "b", "c"));

// ❌ 遍历时直接删除 → ConcurrentModificationException
for (String s : list) {
if ("b".equals(s)) list.remove(s);
}

// ✅ 用 Iterator.remove()
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("b".equals(it.next())) it.remove();
}

// ✅ Java 8+ removeIf
list.removeIf("b"::equals);
fail-fast vs fail-safe
  • fail-fast:ArrayList、HashMap 的迭代器,检测到并发修改立即报错
  • fail-safe:CopyOnWriteArrayList、ConcurrentHashMap 的迭代器,遍历的是快照/弱一致性视图,不报错

详见 迭代器与 fail-fast


常见面试问题

Q1: Iterator 和 ListIterator 的区别?

答案

维度IteratorListIterator
方向只能向前可以双向(hasPrevious/previous
修改只能 remove可以 addsetremove
索引可获取当前索引
适用所有 Collection仅 List

Q2: for-each 循环的原理?

答案

编译器将 for-each 转换为 Iterator 调用。对于数组则转换为普通 for 循环。所以任何实现了 Iterable 接口的类都能用 for-each。

Q3: Stream 和 Iterator 的区别?

答案

维度IteratorStream
风格命令式(外部迭代)声明式(内部迭代)
复用迭代器用完需重新获取Stream 只能消费一次
并行手动实现parallelStream()
惰性求值不支持中间操作惰性执行

详见 Stream API

相关链接