“遍历集合不用 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
是否等于 expectedModCount
ConcurrentModificationException
cursor
指针移动,返回元素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()
时触发 ConcurrentModificationException
list.remove()
,modCount
会变,但 expectedModCount
不变,下一次 next()
就会抛异常“一旦发现集合在迭代过程中被外部修改,立即抛出异常,而不是等到不确定的未来。”
这是 Java 集合框架的一种安全保护机制。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
expectedModCount
在 Iterator
创建时被初始化为当前 modCount
add
、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)、线程模型 等深刻的设计思想。