Java 异常 ConcurrentModificationException

Java 中,时常遇到在循环中修改数据时,出现并发修改异常 ConcurrentModificationException。

异常成因

本质上,ConcurrentModificationException 用于在我们迭代的内容被修改时快速失败。让我们通过一个简单的测试来证明这一点:

public void cmException()  {

    List<Integer> integers = new ArrayList(1, 2, 3, 4);

    for (Integer integer : integers) {
        integers.remove(1);
    }
}

在完成迭代之前,我们尝试删除一个元素,这会触发异常 ConcurrentModificationException。

异常处理

如果我们真的想在迭代时从集合中删除元素,可以有以下这些解决方案。

使用迭代器

原始的迭代器 Iterator 在删除元素时,不会导致 ConcurrentModificationException。

for (Iterator<Integer> iterator = integers.iterator(); iterator.hasNext();) {
    Integer integer = iterator.next();
    if(integer == 3) {
        iterator.remove();
    }
}

不过要注意,这里使用的是 iterator 的 remove 删除方法,而不是 List 的 remove 删除方法。

记录删除元素

迭代器 Iterator 在写法上有点冗长,我们可以采用先遍历在删除的方式实现:

List<Integer> list = new ArrayList(1, 2, 3, 4);
List<Integer> needRemove = new ArrayList();

for (Integer integer : list) {
    if(integer == 3) {
        toRemove.add(integer);
    }
}
list.removeAll(toRemove);

使用 removeIf

Java 8 向Collection接口引入了removeIf()方法。这意味着如果我们使用它,我们可以使用函数式编程的思想再次达到相同的结果:

List<Integer> list = new ArrayList(1, 2, 3, 4);

list.removeIf(i -> i == 3);

使用 stream

stream 流可以方便地过滤出我们想要的数据:

List<Integer> list = new ArrayList(1, 2, 3, 4);

List<String> results = list
  .stream()
  .filter(i -> i != 2)
  .map(Object::toString)
  .collect(toList());

使用 stream 可以进行更多的操作,例如可以将 Integer 的集合变成 String 的集合,并且过滤掉了不需要的数据。

转载请注明出处:码谱记录 » Java 异常 ConcurrentModificationException
标签: