在遍历集合时,如果进行了增删操作,有时会抛出 java.util.ConcurrentModificationException异常。这类异常也被称为 fail-fast,它是Java集合的一种错误检测机制。
出现异常的原因:使用了iterator和foreach(底层也是使用了iterator的方式)进行遍历,并且遍历期间进行add或remove操作,那就有可能发生此异常。出现异常的关键就在于iterator的next()方法,下面是其源码
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
其中的checkForComodification()方法会检查集合的大小,如何跟预期的,也就是与list.iterator()调用时的集合大小不一致,就是抛异常。
单线程下的解决方法
// 使用最原始的下标遍历方式
for ( int i = 0; i < myList.size(); i++) {
String value = myList.get(i);
if (value.equals( "3")) {
myList.remove(value); // ok
i--; // 因为位置发生改变,所以必须修改i的位置
}
}
// 使用iterator的remove方法,而不使用集合本身的remove方法
for (Iterator<string> it = myList.iterator(); it.hasNext();) {
String value = it.next();
if (value.equals( "3")) {
it.remove(); // ok
}
}
// 把需要删除的元素添加到临时集合中,遍历结束后使用removeAll方法删除
List<string> templist = new ArrayList<string>();
for (String value : myList) {
if (value.equals( "3")) {
templist.remove(value); }
}
}
myList.removeAll(templist);
多线程下的解决方法尽量使用java.util.concurrent包下的集合,不推荐使用synchronizer来加锁或者使用Collections.synchronizedList,效率比较慢,会导致多个线程遍历时阻塞。推荐使用CopyOnWriteArrayList,对于map,在多线程情况下,也推荐使用ConcurrentHashMap。
List<string> myList = new CopyOnWriteArrayList<string>();
myList.add("a");
myList.add("b");
myList.add("c");
for (int i = 0; i < myList.size(); i++) {
String value = myList.get(i);
if (value.equals( "3")) {
myList.remove(value);
i--; // 记得下标减1,使得下次遍历能遍历到删除元素的下个元素,否则会跳过该元素
}
}