例子
for(Integer integer:list){
System.out.println(integer);
if(integer.equals(0)){
list.remove(integer);
}
}
会报错
for(Integer integer:list){
System.out.println(integer);
if(integer.equals(3)){
list.remove(integer);
}
}
[0, 1, 2, 4]
代码根本上没有什么区别,只是在删除的条件判断不太一样,那到底为什么会出现这样问题呢?
foreach 循环实际上是用的 iterator 迭代器迭代
源码
private class Itr implements Iterator<E>{
intcursor;// index of next element to return
intlastRet=-1;// index of last element returned; -1 if no such
intexpectedModCount=modCount;
// prevent creating a synthetic constructor
Itr(){}
public booleanhasNext(){
returncursor!=size;
}
@SuppressWarnings(“unchecked”)
public Enext(){
checkForComodification();
含义
modCount— 在父类AbstractList中初始化为0,表示集合修改次数的期望值
cursor –迭代器的游标,元素的索引值,初始值为0
lastRet –返回最后一个元素的索引值、如果没有找到则返回-1
expectedModCount –修改次数的期望值,可以看到在迭代器初始化时,这个属性就被赋值为当前修改次数的值了。
checkForComodification –此方法用来检查修改次数是否发生变化,从而判断是否需要触发异常。
在迭代过程中,每一次迭代,cursor会+1, 而hasNext()会判断是否存在下一个元素、next()获取下一个元素的值,最终直到不存在下一个元素,则迭代结束。
看到报错的原因在于进入next中的 checkForComodification()报错了即:
ArrayList.this.modCount != this.expectedModCount
具体的原因是:如果你在遍历过程中删除元素,集合中modCount就会变化,但是迭代器中的expectedModCount没有改变所以报错了。
我们怎样来删除元素呢?—-可以使用迭代器里面的remove()方法
public void remove(){
if(lastRet<0)
throw newIllegalStateException();
checkForComodification();
try{
ArrayList.this.remove(lastRet);
cursor=lastRet;
lastRet=-1;
expectedModCount=modCount;// 处理expectedModCount
}catch(IndexOutOfBoundsException ex){
throw newConcurrentModificationException();
}
可以看见的 expectedModCount = modCount;也同时被修改
iterator.remove();