“遍历集合不用 Iterator,就像开车不用方向盘。”
Iterator是 Java 集合框架中最基础、最常用的接口之一。 你可能每天都在用for-each循环遍历ArrayList或HashSet,但你是否想过: 它是如何工作的?为什么删除元素时必须用iterator.remove()?ConcurrentModificationException又是怎么产生的?
本文将带你深入 JDK 源码,从底层实现、设计模式到线程安全,全面剖析 Iterator 的工作原理。
Iterator 接口定义与基本使用ArrayList 中的 Iterator 实现源码解析fail-fast 机制:快速失败是如何实现的?remove() 方法的线程安全性问题ListIterator 与双向遍历foreach 循环与 Iterator 的关系Iterator 是 java.util 包中的一个核心接口,定义了集合遍历的基本行为:
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); // 可选操作
default void forEachRemaining(Consumer<? super E> action);
}方法 | 说明 |
|---|---|
hasNext() | 是否还有下一个元素 |
next() | 返回下一个元素,指针后移 |
remove() | 删除当前元素(调用 next() 后才能调用) |
forEachRemaining() | JDK 8+,对剩余元素执行操作 |
💡 注意:
remove()是 可选操作,某些集合(如Arrays.asList()返回的列表)不支持删除,调用会抛出UnsupportedOperationException。
我们以最常用的 ArrayList 为例,深入其内部实现。
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> it = list.iterator(); // 获取迭代器我们进入 ArrayList.iterator() 源码(JDK 17):
public Iterator<E> iterator() {
return new Itr();
}它返回了一个内部类 Itr 的实例。
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回的元素索引
int lastRet = -1; // 最近一次返回的元素索引
int expectedModCount = modCount; // 期望的修改次数
// ...
}字段 | 说明 |
|---|---|
cursor | 游标,指向下一个要读取的元素 |
lastRet | 上一次 next() 返回的元素索引 |
expectedModCount | 关键!用于 fail-fast 检查 |
🔑
modCount是ArrayList父类AbstractList中的字段,记录集合被结构性修改的次数(如add、remove)。
hasNext() 与 next() 方法public boolean hasNext() {
return cursor != size;
}
public E next() {
checkForComodification(); // 检查并发修改
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
lastRet = i;
return (E) elementData[lastRet];
}checkForComodification():检查 modCount 是否等于 expectedModCountConcurrentModificationExceptioncursor 指针移动,返回元素remove() 方法public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); // 调用 ArrayList 的 remove
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 同步修改次数
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}iterator.remove()?expectedModCount,避免下次 next() 时触发 ConcurrentModificationExceptionlist.remove(),modCount 会变,但 expectedModCount 不变,下一次 next() 就会抛异常“一旦发现集合在迭代过程中被外部修改,立即抛出异常,而不是等到不确定的未来。”
这是 Java 集合框架的一种安全保护机制。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}expectedModCount 在 Iterator 创建时被初始化为当前 modCountadd、remove 操作,modCount++Iterator 每次操作前检查两者是否相等⚠️ 注意:fail-fast 不是线程安全的保证!它只能检测到单线程下的意外修改,多线程环境下依然可能漏判。
ListIterator 继承自 Iterator,支持双向遍历和元素替换:
public interface ListIterator<E> extends Iterator<E> {
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void set(E e);
void add(E e);
}ListIterator<String> lit = list.listIterator(list.size());
while (lit.hasPrevious()) {
System.out.println(lit.previous());
}ArrayList 中的 ListItr 实现与 Itr 类似,但维护了双向指针。
你写的:
for (String s : list) {
System.out.println(s);
}在编译后,会被自动转换为 Iterator 形式:
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}✅ 所以:
foreach循环的本质就是Iterator!
Iterator 不是线程安全的// 线程1
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next(); // 可能抛 ConcurrentModificationException
}
// 线程2
list.add("D"); // 外部修改场景 | 推荐方案 |
|---|---|
单线程遍历删除 | 使用 iterator.remove() |
多线程环境 | 使用 CopyOnWriteArrayList |
高并发读写 | 使用 ConcurrentHashMap、BlockingQueue 等并发集合 |
CopyOnWriteArrayList的Iterator是 fail-safe 的,基于快照遍历,不会抛ConcurrentModificationException。
机制 | 原理 | 注意事项 |
|---|---|---|
迭代指针 | cursor 指向下个元素 | hasNext() 判断是否越界 |
fail-fast | modCount vs expectedModCount | 检测意外修改 |
remove() 安全性 | 同步更新 expectedModCount | 必须先 next() 才能 remove |
foreach 本质 | 编译为 Iterator | 不能在循环中修改集合 |
线程安全 | 非线程安全 | 多线程用并发集合 |
iterator.remove()foreach 循环中调用 list.remove()Concurrent 包下的集合fail-fast 是“检测”而非“防止”并发修改Iterator 虽然只是一个简单的接口,但其背后蕴含着 设计模式(迭代器模式)、安全机制(fail-fast)、线程模型 等深刻的设计思想。