禁止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

posted @ 2019-05-13 16:32  Diamond-Shine  阅读(610)  评论(0编辑  收藏  举报