在一般的集合框架中(比如ArrayList),如果要删除集合总的某一条件元素,一般调用的都是迭代器的remove方法。
使用for循环,和增强for循环一般都会抛出ConcurrentModificationException异常。
增强for循环删除元素代码:
List<String> testList = new ArrayList<>();
for(int i = 0;i<10;i++){
testList.add("test : "+i);
}
for (String string : testList) {
System.out.println(string);
testList.remove(string);
}
增强for开始testList数据(modCount=10,size=10):
[test : 0, test : 1, test : 2, test : 3, test : 4, test : 5, test : 6, test : 7, test : 8, test : 9]
第一次执行remove可以正常删除,这时testList数据(modCount=11,size=9):
[test : 1, test : 2, test : 3, test : 4, test : 5, test : 6, test : 7, test : 8, test : 9]
第二次循环的时候就会抛出ConcurrentModificationException异常。
增强for循环其实也是调用的迭代器,具体可以看编译后的文件或者debug循环过程可以看出,而且这个迭代器对于这个for循环
只会在初始化的时候创建,所以以后的每次for循环都会使用同一个迭代器对象。
在迭代器创建的时候,有一个变量:expectedModCount 这个值在迭代器初始化的时候 设置expectedModCount = modCount,
当testList.remove发生的时候,没有办法通知迭代器的expectedModCount发生变化,所以第二次循环获取next的时候,因为checkForComodification
方法校验了这两个值是不是一样的,由于不一致导致抛出ConcurrentModificationException异常。
就像下面的写法一样:
Iterator<String> iterator = testList.iterator();
while(iterator.hasNext()){
String obj = iterator.next(); //获取值,并且使迭代器游标cursor+1
testList.remove(obj); // throw ConcurrentModificationException
iterator.remove(); //不获取next直接remove会抛出IllegalStateException异常
}
直接使用迭代器的remove可以成功是因为迭代器的每次remove都会设置expectedModCount = modCount,remove方法之前要确保next方法调用。
如果一定要使用循环删除元素,不使用迭代器,可以选择使用CopyOnWriteArrayList 这个可以正常的在循环中删除元素,每次的操作都有数组的copy,性能
会有所下降。
其他的集合类也大部分是这样的情况,所以还是尽量的使用迭代器比较好。