美文网首页
ArrayList 在迭代期间修改集合的内容,抛出 Concur

ArrayList 在迭代期间修改集合的内容,抛出 Concur

作者: wuchao226 | 来源:发表于2020-08-05 19:51 被阅读0次

故障原因

ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常

故障代码

public class ArrayListTest {

    public static void main(String[] args) {

        List<String> lists = new ArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                lists.remove(next);
            }
        }
    }
}

ArrayList 会抛出异常的原因

在 ArrayList 源码里的内部类 Itr 的 next 方法中有一个 checkForComodification 方法,代码如下:

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

modCount 是修改记录数,expectedModCount 是期望修改记录数,初始化时expectedModCount = modCount。

这里会首先检查 modCount 是否等于 expectedModCount。modCount 是保存修改次数,每次我们调用 add、remove 或 trimToSize 等方法时它会增加,expectedModCount 是迭代器的变量,当我们创建迭代器时会初始化并记录当时的 modCount。后面迭代期间如果发现 modCount 和 expectedModCount 不一致,就说明有人修改了集合的内容,就会抛出异常。

和 ArrayList 不同的是,CopyOnWriteArrayList 的迭代器在迭代的时候,如果数组内容被修改了,CopyOnWriteArrayList 不会报 ConcurrentModificationException 的异常,因为迭代器使用的依然是旧数组,只不过迭代的内容可能已经过时了

解决方案

解决方案1

使用 iterator 的 remove 操作替代 ArrayList 集合自己的 remove 操作

public class ArrayListTest {

    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                iterator.remove();
            }
        }
    }
}

分析:通过查看 iterator 的 remove 方法发现,其实还是调用了 ArrayList 集合的remove方法移除元素,但是会使 expectedModCount = modeCount 所以不会抛出 ConcurrentModificationException 异常

解决方案2

使用 CopyOnWriteArrayList 并发集合类

public class ArrayListTest {

    public static void main(String[] args) {
        List<String> lists = new CopyOnWriteArrayList<>();
        lists.add("a");
        lists.add("b");

        Iterator<String> iterator = lists.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            if (next == "b") {
                lists.remove(next);
                System.out.println(lists);
            }
        }
    }
}

因为迭代器中没有 checkForComodification 操作,并且集合的 add 和 remove 方法中都通过 ReentrantLock 加锁保证并发操作下的安全性。

在对ArrayList集合进行并发操作时尽量使用CopyOnWriteArrayList集合类代替

相关文章

网友评论

      本文标题:ArrayList 在迭代期间修改集合的内容,抛出 Concur

      本文链接:https://www.haomeiwen.com/subject/vfborktx.html