【阿里规范】不要在 foreach 循环里进行元素的 remove/add 操作

这几天没事的时候把阿里的Java开发手册大概看了一遍,其中有一条如下:

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

正例:

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素条件) {
iterator.remove();
}
}

反例:

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}

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

  这个规则我之前是知道的,但是看到上面这个说明,就有点不太淡定了,难道还会有什么奇葩的事不成。于是把这行代码考到IDEA运行了一下,正例自然是不必说。

  果然remove元素1的时候正常,remove 2java.util.ConcurrentModificationException异常。然后我又添加元素3,这下情况又变了,remove 13异常,remove 2正常。发现一个规律,remove倒数第二元素正常,其他元素就会抛异常。

  因为foreach这种遍历的语法,本身就是Java的语法糖,我们要看真实的代码实现,上述反例经过Java编译器编译后实际上代码如下:

ArrayList list = new ArrayList();
list.add("1");
list.add("2");
Iterator i$ = list.iterator();
while(i$.hasNext()) {
String item = (String)i$.next();
if("2".equals(item)) {
list.remove(item);
}
}

简单的打了几个断点发现,问题和hasNext()有很大关系。 看一下ArrayList的hasNext()的实现

public boolean hasNext() {
return cursor != size;
}

看ArrayList的源码,很多方法比如:next()previous()remove()在执行前都会先执行以下方法:

final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}

简单的说checkForComodification()是为了保证访问的一致性。 下面我们通过人肉执行循环来看一下这其中报异常或者不报异常的原因 remove 1的循环如下:

步骤代码表达式值cursorsize备注
1i$.hasNext()true02
2String item = (String)i$.next();-12
3if("1".equals(item))true12
4list.remove(item);-11
5i$.hasNext()false11此时循环结束,走不到下一个next()因此也不会抛出异常

remove 2的循环如下:

步骤代码表达式值cursorsize备注
1i$.hasNext()true02
2String item = (String)i$.next();-12
3if("2".equals(item))false12
4i$.hasNext()true12
5String item = (String)i$.next();-22
6if("2".equals(item))true22
7list.remove(item);-21remove操作完成后modCount != expectedModCount,因为没有执行到next()方法,此时还不会抛出异常
8i$.hasNext()true21
9String item = (String)i$.next();-21此时抛出异常

现在终于明白为什么remove倒数第二个元素不报错的原因了,因为remove倒数第二元素后刚好cursor == size,此时hasNext()返回false。

关于modCountexpectedModCount可参考如下资料:

posted @   ThinkStu  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示