笔者在阅读阿里巴巴Java开发手册时看到这样一段话:

不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用Iterator 方式,如果并发操作,需要对 Iterator 对象加锁
正例:

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
	String item = iterator.next();
	if (删除元素的条件) {
		iterator.remove();
	}
}

反例:

for (String item : list) {
	if ("1".equals(item)) {
		list.remove(item);
	}
}

说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗?

笔者疑惑,什么意思,难道不是所有的增强for删除元素都会报错吗?记得是modCount != expectedModCount的时候会报错呀

于是笔者实现了一下,发现…

List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");

        for (String item : list) {
            if ("1".equals(item)) {
                list.remove(item);
            }
        }
        System.out.println(list);

发现居然…

pythonfor循环 删除指定元素_List


一条小绿条震惊了我

pythonfor循环 删除指定元素_List_02


然后我执行第二套代码,也就是把1换成2

List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");

        for (String item : list) {
            if ("2".equals(item)) {
                list.remove(item);
            }
        }
        System.out.println(list);

pythonfor循环 删除指定元素_List_03


呼~报错了,是我满意的结果。

于是,我决定debug一下发生了什么,第一套代码为什么不会报错呢。

首先进到remove方法

pythonfor循环 删除指定元素_List_04


很合理,进到fastRemove方法了

pythonfor循环 删除指定元素_java_05


modCount++,移动元素,让空出的位置值null让GC清理,很合理,1 被删除了。

pythonfor循环 删除指定元素_删除元素_06


然后回来,return true,进行下一个遍历。增强for是会用ArrayList的内部类,来判断是否有下一个元素,cursor是一个游标,判断是否遍历到头了。

pythonfor循环 删除指定元素_java_07


可以看到,由于刚才已经删除了“1”,所以size为1,而cursor由于是到索引1了,haxNext的判断游标与长度是相等的,在他看来遍历结束了,返回false。于是遍历结束,这样还没进入到modCount和expectedModCount的比较,遍历就已经退出~

让我们再来看看第二套代码

由于第一个值是“1”,不相等,所以直接进入第二个循环

pythonfor循环 删除指定元素_java_08


跟前面一样,会用ArrayList的内部类判断是否遍历结束

pythonfor循环 删除指定元素_List_09


这里游标为1,size为2,所以不相等,为true接下来是调用next方法,这个方法依旧是ArrayList的内部类Itr持有

pythonfor循环 删除指定元素_pythonfor循环 删除指定元素_10


进入checkForComodification方法

pythonfor循环 删除指定元素_内部类_11


可以看到,由于之前没有删除操作,所以这一步是ok的,不会报错。继续走next剩余的方法,没有问题。

pythonfor循环 删除指定元素_删除元素_12

接下来因为相等了,执行remove方法

pythonfor循环 删除指定元素_List_13


pythonfor循环 删除指定元素_List_14


执行fastRemove方法,modCount的值变化

pythonfor循环 删除指定元素_内部类_15


然后增强for循环继续

pythonfor循环 删除指定元素_List_16


发现hasNext依旧成立,因为游标cursor为2,而长度为1

于是执行next方法,首先执行checkForComodification

pythonfor循环 删除指定元素_内部类_17


modCount不等于expectedModCount,抛出异常

pythonfor循环 删除指定元素_删除元素_18

总结: 原来如此,代码1之所以没有报错,原来是因为它没有进入到modCount和expectedModCount的判断就结束了。但是只要我们明确不能在增强for循环删除、插入元素,还是ok的~。

果然,要了解底层,还是得自己看看源码才知道。