Java 集合遍历过程中修改数据触发 Fail-Fast 机制 ,导致报ConcurrentModificationException异常
Java Fail-Fast 机制
Fail-Fast 机制是 Java 集合框架中的一种错误检测机制,用于在遍历集合时检测结构修改。如果在迭代器创建之后,集合被修改(例如添加或删除元素),并且这种修改不是通过迭代器自身的 remove()
方法进行的,那么迭代器会立即抛出 ConcurrentModificationException
异常,以防止不一致或不可预测的行为。
工作原理
-
修改计数器:
- 集合类(如
ArrayList
、HashMap
等)内部维护一个modCount
计数器,记录集合被结构性修改的次数(结构性修改包括添加或删除元素,但不包括通过迭代器自身的remove()
方法进行的删除)。
- 集合类(如
-
迭代器的预期修改计数:
- 当创建迭代器时,迭代器会记录当前集合的
modCount
值,作为其expectedModCount
。 - 在每次调用迭代器的
next()
方法时,迭代器会检查expectedModCount
是否与集合的当前modCount
一致。
- 当创建迭代器时,迭代器会记录当前集合的
-
检测不一致:
- 如果在迭代过程中,集合的
modCount
发生变化(即expectedModCount
不等于modCount
),迭代器会立即抛出ConcurrentModificationException
异常。
- 如果在迭代过程中,集合的
示例代码
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class FailFastExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
// 在迭代过程中修改集合,会抛出 ConcurrentModificationException
list.add("D");
}
}
}
输出:
A
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at FailFastExample.main(FailFastExample.java:13)
解决方法
-
使用迭代器自身的
remove()
方法:- 如果需要在遍历过程中删除元素,应使用迭代器的
remove()
方法,而不是直接操作集合。
Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); if (element.equals("B")) { iterator.remove(); // 安全删除元素 } }
- 如果需要在遍历过程中删除元素,应使用迭代器的
-
使用线程安全的集合类:
- 使用
java.util.concurrent
包中的线程安全集合类,如CopyOnWriteArrayList
、ConcurrentHashMap
等。
import java.util.concurrent.CopyOnWriteArrayList; public class ConcurrentExample { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // 在迭代过程中修改集合,不会抛出 ConcurrentModificationException list.add("D"); } } }
- 使用
-
使用
Collections.synchronizedList()
或Collections.synchronizedSet()
:- 将集合包装为线程安全的集合。
import java.util.Collections; import java.util.Iterator; import java.util.List; public class SynchronizedExample { public static void main(String[] args) { List<String> list = Collections.synchronizedList(new ArrayList<>()); list.add("A"); list.add("B"); list.add("C"); synchronized (list) { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // 在迭代过程中修改集合,不会抛出 ConcurrentModificationException list.add("D"); } } } }
注意事项
-
单线程环境:
- 在单线程环境中,Fail-Fast 机制有助于及时发现集合被意外修改的问题。
- 但需要注意在迭代过程中不要直接修改集合,除非使用迭代器自身的
remove()
方法。
-
多线程环境:
- Fail-Fast 机制在多线程环境中可能会导致
ConcurrentModificationException
异常。 - 应使用线程安全的集合类或同步机制来避免此类问题。
- Fail-Fast 机制在多线程环境中可能会导致
-
性能影响:
- Fail-Fast 机制本身对性能的影响较小,主要体现在每次迭代时的
modCount
检查。 - 但在多线程环境下,频繁的同步操作可能会显著影响性能。
- Fail-Fast 机制本身对性能的影响较小,主要体现在每次迭代时的
总结
- Fail-Fast 机制 是 Java 集合框架中用于检测集合在迭代过程中被修改的一种机制。
- 通过在迭代过程中抛出
ConcurrentModificationException
异常,Fail-Fast 机制可以及时发现不一致的行为,确保集合的完整性和一致性。 - 在使用 Fail-Fast 机制时,需要注意在迭代过程中不要直接修改集合,除非使用迭代器自身的
remove()
方法。 - 对于多线程环境,建议使用线程安全的集合类或同步机制来避免
ConcurrentModificationException
异常。
通过合理使用 Fail-Fast 机制,可以提高代码的健壮性和可靠性。