在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出ConcurrentModificationException。如下:
List list = new ArrayList<>();
for(int i = 0;i < 10;i++){
list.add(i);
}
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Integer next = iterator.next();
if(next==3){
list.remove(6);
}
System.out.println(next);
}
输出结果为:
0
1
2
3
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
at Main.main(Main.java:7)
原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值(具体可看Java从入门到放弃(五)集合框架之ArrayList源码(2))。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
所以不要在迭代遍历的时候直接删除集合内的元素,如果要删除可以使用迭代器内部的方法:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); 检查modCount的值是否发生改变
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; //更新expectedModCount的值,
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
因为内部的remove方法会同步更新expectedModCount的值,使用不会发生快速失败的异常。
安全失败是在迭代的时候对集合进行更新的时候不操作原集合,而是把集合进行拷贝一份,在复制的集合上进行修改操作,修改完之后再更改集合对象的引用。所以就避免了ConcurrentModificationException异常,
不过因为不是再原集合上操作,所以在迭代的时候如果对集合进行了修改,迭代器是不能检测到的,
java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改),如:ArrayList,LinkedList
java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。如:CopyOnWriteArrayList。
Integer delete = 6;
List list = new ArrayList<>();
for(int i = 0;i < 10;i++){
list.add(i);
}
list.remove(delete);
System.out.println(list.toString()); //[0,1,2,3,4,5,7,8,9]
因为remove重载有两个方法,一个remove(int),一个是remove(object),代码中的delete是Integer类型,所以会视为Object,直接删除数组中的6这个元素,而不是索引为6的元素。
long delete = 6;
List list = new ArrayList<>();
for(int i = 0;i < 10;i++){
list.add(i);
}
list.remove(delete);
System.out.println(list.toString()); //[0,1,2,3,4,5,6,7,8,9]
这里的delete虽然是基本数据类型,但是remove(int)方法的参数是int,long不能自动转换为int类型,需要强制转换,所以list.remove(delete)是会使用list.remove(object)方法,因为参数是Long类型,集合内数据是Integer类型,所以找不到对应的相等的元素,没有删除任何元素。
1、Iterator适用于全部的集合类,而ListIterator只能适用于List及其之类使用
2、Iterator迭代器只能向后遍历,而ListIterator不仅可以向后遍历,也能向前遍历。
3、ListIterator可以定位当前位置的索引,而Iterator不能
4、ListIterator可以remove,add,set方法对集合进行修改,而Iterator只有remove方法;