需实现iterable接口并维护modcount与expectedmodcount一致性,结构性修改时递增modcount,迭代器通过checkforcomodification校验二者是否相等,不等则抛concurrentmodificationexception。

要在 Java 中让自研集合类支持 for-each 循环并满足 fail-fast 机制,核心是让集合实现 Iterable 接口,同时其迭代器(Iterator)在检测到结构修改时主动抛出 ConcurrentModificationException。这不是靠“自动触发”,而是由迭代器主动检查和校验。
让自研集合实现 Iterable 并返回安全的 Iterator
for-each 本质是编译器语法糖,会调用集合的 iterator() 方法。因此你的集合类必须:
- 实现
Iterable<e></e>接口 - 在
iterator()方法中返回一个自定义的Iterator<e></e>实现 - 该迭代器需维护一个“期望修改次数”(
expectedModCount),与集合自身的modCount对齐
例如,假设你有一个简单数组-backed 集合:
public class MyListprivate Object[] elements = new Object[10];
private int size = 0;
private int modCount = 0; // 记录结构性修改次数
public Iterator
return new MyIterator();
}
private class MyIterator implements Iterator
private int cursor = 0;
private final int expectedModCount = modCount; // 创建时快照
public boolean hasNext() {
checkForComodification();
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
if (!hasNext()) throw new NoSuchElementException();
return (E) elements[cursor++];
}
private void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
public boolean add(E e) {
ensureCapacity();
elements[size++] = e;
modCount++; // 每次结构性修改都递增
return true;
}
// 其他方法(remove、clear 等)也需更新 modCount
}
所有结构性修改操作必须更新 modCount
fail-fast 不是魔法,它依赖“修改计数器”的一致性。只要集合结构发生变化(如增、删、清空),就必须增加 modCount。常见遗漏点包括:
-
clear():必须设size = 0且modCount++ -
remove(int index)或remove(Object o):移动元素后要modCount++ - 批量操作如
addAll()、retainAll():每次引起结构变化都要递增
注意:仅改变元素内容(如 set(index, e))不属于结构性修改,一般不更新 modCount —— 这与 ArrayList 行为一致。
避免在迭代过程中修改集合本身
即使实现了 fail-fast,也不能依赖它来“允许”边遍历边修改。正确做法是:
- 若需删除,使用迭代器自身的
remove()方法(需在next()后调用,且保证线程安全) - 若需添加或复杂变更,先收集待操作元素,循环结束后再批量处理
- 多线程场景下,fail-fast 不等于线程安全;应配合同步机制或使用
java.util.concurrent包类型
例如,在 for-each 中直接调用 myList.add(x) 会导致下一次 next() 抛异常 —— 这正是 fail-fast 的保护作用,不是 bug,而是设计意图。
测试 fail-fast 行为是否生效
写一个简单测试验证机制是否工作:
MyListlist.add("a"); list.add("b");
for (String s : list) {
System.out.println(s);
if ("a".equals(s)) {
list.add("c"); // 触发结构修改
}
}
// 下一轮 next() 将抛 ConcurrentModificationException
如果未抛异常,说明 modCount 未更新、checkForComodification() 未被调用,或快照逻辑有误。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











