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的性能并不是很好。因此,在使用的时候,需要根据具体情况进行取舍。