今天遇到一个问题,代码如下:
public class TestIter {
public static void main(String[] args) {
List<String> list = new ArrayList<>();;
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String string = iterator.next();
//aa bb dd均报错,唯独cc没问题
if(string.equals("dd")) {
list.remove(string);
}
System.out.println(list.toString());
}
}
}
创建了一个List<String>
,然后给他add
了4个元素,将list
转为Iterator
然后进行遍历,在遍历过程中对list进行remove
,在测试的过程中,发现居然会出现报错现象,一直想不明白,后来根据报错信息去查找源码才发现问题所在。
先展示一下报错信息:
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at ace.zyi.yam.demo.TestIter.main(TestIter.java:17)
通过报错信息,我们可以看出执行next()
方法的时候会进入checkForComodification()
方法,这时候就报错了,那我们来看看这个方法内源码是怎么样的。。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
看源码我们可以看到,之所以会报错是因为modCount != expectedModCount
,那这两个变量分别是什么意思呢?
实际上,在我们将list
转为Iterator
的时候,会将modCount
赋值给expectedModCount
,这是iterator
里的一个字面量,保存生成Iterator的时候,list中修改元素的次数,
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
通过查看add
方法,我们可以看出,每进行一次add
操作,就会进行自增,刚才我进行了4次,所以modCount
是4,在生成Iterator的时候,expectedModCount
也是4,
在遍历的时候,一定会经过两个方法,分别是hasNext()
和next()
public boolean hasNext() {
return cursor != size;
}
cursor:
这是Itr类(Iterator的一个子类),用来保存Iterator
当前的位置信息,从0开始。size:
这是ArrayList
的字段,因为Itr
是一个内部类,所以直接调用了ArrayList
的size字段。
实际上这就是当cursor != size
的时候,就默认后面还有元素。
所以当我们进行remove
删除元素的时候,modCount
一样会进行自增,而原本是4,进行一次remove就是5了,所以当下一个元素进来执行next()
方法的时候,就会出现modCount != expectedModCount
了,所以就会报异常出来了。
但是是不是删除所有元素又都会报错呢?那又不是的,从我刚开始贴的代码就可以看到,倒数第二个元素是可以正常remove
,而且还不会报异常的。
其实也是一个挺简单的道理,根据上面的分析,进入next()
方法就会报异常,那我们在删除了元素以后,不进入这个方法就不会报异常了,也就是说hasNext()
返回false,就会直接终止循环。
我们分析一下为什么倒数第二个不会报异常,看源码可以看出,倒数第二个元素的时候,Itr的cursor
是3,当list删除掉一个元素的时候,size
也是等于3,所以在判断的时候,cursor==size
,因此就会在删除了一个元素以后,直接跳出循环,所以不会报异常了。
如果是使用Iterator的remove()
方法就不会报异常了,因为这个方法里面对expectedModCount
重新赋值。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; //重新赋值
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
作者:KingJack灬
链接:https://www.jianshu.com/p/747b30ba5b4c
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最后
以上就是专一雪碧最近收集整理的关于Iterator/foreach遍历list时,删除list元素的时候报错问题的全部内容,更多相关Iterator/foreach遍历list时,删除list元素内容请搜索靠谱客的其他文章。
发表评论 取消回复