禁止foreach循环使用remove/add----快速失败
阿里巴巴开发手册中有一条:
7【强制】不要在 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”,会是同样的 结果吗?
代码重现:
public static void main(String[] args) { List<String> list = Lists.newArrayList("a","b","c","d"); for (String userName : list) { if (userName.equals("a")) { list.remove(userName); list.add(userName); } } }
结果:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at com.it.test.TestUnit.main(TestUnit.java:23)
异常:
/*
*modCount:实际修改次数
*expectedModCount:期望修改次数
*/
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
这个就是就是快速失败机制——fail-fast
原因:
出现这个异常的原因,是因为foreach循环中,通过iterator修改了集合,但是元素的add/remove却是直接使用的集合类自己的方法。通过反编译得知,foreach通过while和Iterator实现的。
这就导致iterator在遍历的时候,会发现有一个元素在自己不知不觉的情况下就被删除/添加了,就会抛出一个异常,用来提示用户,可能发生了并发修改。
解决:
1、使用普通for循环进行操作,因为普通for循环并没有用到Iterator的遍历,所以压根就没有进行fail-fast的检验。
2、直接使用Iterator进行操作
3、使用Java 8中提供的filter过滤
4、直接使用fail-safe的集合类
5、使用增强for循环其实也可以
以上这五种方式都可以避免触发fail-fast机制,避免抛出异常。如果是并发场景,建议使用concurrent包中的容器,如果是单线程场景,Java8之前的代码中,
建议使用Iterator进行元素删除,Java8及更新的版本中,可以考虑使用Stream及filter。
原文地址:https://mp.weixin.qq.com/s/e9ITxUmsMFhfjeHhOgTtfA