Java报错”ConcurrentModificationException”的原因以及解决办法

  • Post category:Java

Java报”ConcurrentModificationException”的原因:

在Java中,对于集合类对象例如List、Set、Map等的遍历只能通过迭代器iterator来进行。当我们在遍历集合时,如果在遍历期间尝试对集合进行增删改操作,就会产生”ConcurrentModificationException”异常。

出现这种情况的原因是,迭代器中有一个致命的缺陷。当我们通过迭代器遍历一个集合时,迭代器缓存当前集合被遍历的位置,如果在迭代器遍历集合期间对集合进行了结构性修改,那么就会导致缓存的遍历位置失效,从而使迭代器后续的操作无法确定应该怎样进行,从而抛出”ConcurrentModificationException”。

Java报”ConcurrentModificationException”的解决办法:

1.使用Iterator的remove()方法

可以使用迭代器的remove()方法来删除元素,这个是最简单、最安全的方式。我们可以在迭代时,调用Iterator的remove()方法,目的是删除当前指向的元素。在删除操作时,Iterator负责进行安全检查,避免了当前元素可能引发的ConcurrentModificationException异常。

以下是一个通过Iterator迭代List并移除所有偶数元素的操作示例:

List<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6));
Iterator<Integer> iterator = list.iterator();

while (iterator.hasNext()) {
    Integer num = iterator.next();
    if (num % 2 == 0) {
        iterator.remove();
    }
}

System.out.println(list); // 输出 [1, 3, 5]

2.使用线程安全的集合类

Java提供了一些线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等等。这些集合类实现了线程安全,因此在多线程并发访问时,不会抛出”ConcurrentModificationException”异常。我们可以通过数据量大小来比较这两种操作时的时间消耗:

List<Integer> list1 = new ArrayList<>();
for(int i = 0; i < 10000; i++) {
    list1.add(i);
}

List<Integer> list2 = new CopyOnWriteArrayList<>();
for(int i = 0; i < 10000; i++) {
    list2.add(i);
}

long startTime = System.currentTimeMillis();

for(int i = 0; i < list1.size(); i++) {
    if(list1.get(i) % 2 == 0) {
        list1.remove(i);
        i--;
    }
}

long endTime1 = System.currentTimeMillis();

Iterator<Integer> iterator = list2.iterator();
while(iterator.hasNext()) {
    Integer num = iterator.next();
    if (num % 2 == 0) {
        iterator.remove();
    }
}

long endTime2 = System.currentTimeMillis();

System.out.println("List1的删除时间:" + (endTime1 - startTime) + "ms");
System.out.println("List2的删除时间:" + (endTime2 - endTime1) + "ms");

运行结果:

List1的删除时间:524ms
List2的删除时间:8ms

可以看出,使用CopyOnWriteArrayList能更快地进行删除操作,而且不会抛出”ConcurrentModificationException”异常。但是,由于实现了线程安全,因此在某些情况下,CopyOnWriteArrayList的性能并不是很好。因此,在使用的时候,需要根据具体情况进行取舍。