通过上述代码,我们可以发现是可以删除成功的
先看foreach反编译后的具体原理代码
可以发现是使用迭代器进行遍历的(表层就是将list集合,你也可以把他看成3颗子弹,装进了手枪的弹夹里,需要遍历的时候就开枪射出)
使用while循环,如果有下一个hasNext为true,则获取下一个的值,var2.next(),同时进行判断,如果是1号,则remove删除,这好像看起来没什么问题,我们再次跟进list也就是ArrayList的源码查看
只要调用了iterator生成迭代器方法就会返回ArrayList类里的Itr实例,并在遍历中使用hasNext,next方法
hasNext:判断当前游标指向的位置cursor,未显示定义值则默认为0,而size是列表的长度为3,因此0!=3,为真则执行var2.next()方法
进入next方法后首先执行checkForComodification,我们继续跟进
这是一个常量无返回值方法,判断modCount!=expectedModCount
-
modCount,列表实际修改次数,modified:修改
-
expectedModCount,期望修改次数(迭代器脑海里认为他已经对该列表修改的次数)
-
我们只要一调用iterator迭代器,则会实例Itr,并且初始值expectedModCount
int expectedModCount = modCount;
我们第一次调用获取到"1号"并删除时,是正常的,但是在remove的时候,他调用的是ArrayList自身的remove方法,体现为 list.remove(member)
可以发现此时modCount++了(add,clear方法也会时modCount自增),第一次迭代遍历结束,进行第二次
hasNext判断,cursor!=size,为真,执行next
执行check判断 真实修改值和迭代器认为修改值做if判断
if (modCount != expectedModCount) throw new ConcurrentModificationException();
问题就出现在这一步,由于expectedModCount是我们一开始调用iterator,并获取实例Itr时由modCount赋予的初始值,而我们的modCount此时已经由list.remove这个操作引起了改变,也就是真实修改次数变化了,此时if判断为真,抛出异常,ConcurrentModificationException:并发修改异常
那么为什么要判断这两个值是否相同呢?从异常名字可以看出,应该是为了防止并发操作/多线程操作对列表进行修改
这其中原因是触发了fail-fast快速失败机制,也就是modCount变量的由来
当字段的值发生意外变化,说明集合已经被修改,数据不一致,此时为了避免这种情况,抛出异常
通俗点理解就是 你有一个游戏账号,每天都得进行签到操作,第一天,你进行了签到,实际签到天数为1(modCount),而你脑子里认为的签到过的次数(expectedModCount)也为1,这并没有什么问题,然而,第二当你想要进行签到的时候,你发现你的实际签到天数居然变成了两天,这与你印象中的签到次数不一致(modCount != expectedModCount),此时你会发现你的账号是否被人盗取,此时你会询问客服具体原因( throw new ConcurrentModificationException();)
那该怎样删除?
-
使用迭代器自己的方法进行删除,原因是他会在删除后更新expectedModCount,从而绕过checkForComodification()里的判断
//给列表装配一个迭代器 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ //获取遍历到的元素 String next = iterator.next(); if ("1号".equals(next)){ //删除当前元素 iterator.remove(); } } //输出查看结果 System.out.println(list);
-
第二种写法
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) { if ("1号".equals(iterator.next())) { iterator.remove(); } } System.out.println(list);
-
第三种,普通for循环的删除
如果不使用i--则会产生如下效果
这里自减的原因是因为删除掉一个元素之后后面的元素会前移一个坐标,与i++抵消,不遍历下一个,否则会产生漏删的情况
或者是从后往前,这样就不会出现后面的元素向前位移的问题了