java数组ArrayList迭代修改的ConcurrentModificationException错误

发布于:2024-06-18 ⋅ 阅读:(134) ⋅ 点赞:(0)

报错:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at net.micah.Test.main(Test.java:20)

我写的demo:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");
        // 隐式创建迭代器 Itr(报错:ConcurrentModificationException) 
        for (String next : list) {
            System.out.println(next);
            if (Objects.equals(next, "d")) {
                list.remove(next);
            }
        }
        System.out.println(list);
        // fori (无法实时拿到数组index更新,导致index对不上,导致错误的修改)
        for (int i = 0 ; i < list.size(); i++) {
            String next = list.get(i);
            System.out.println(next);
            if(Objects.equals(next, "d") || Objects.equals(next, "e")) {
                list.remove(next);
            }
        }
        System.out.println(list);
        // 显式迭代器(安全可靠)
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next);
            if (Objects.equals(next, "d")) {
                iterator.remove();
            }
        }
        System.out.println(list);
    }

}

迭代器内部是闭环的,校验逻辑为:

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

expectedModCount 是在创建 Itr(迭代器内部类) 的时候赋值,为那个瞬间的 modCount

private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

① 从源代码上说,这个 expectedModCount 只在迭代器的内部方法调用时被修改,而且 modCount + 1 时 expectedModCount 会和 modCount 保持同步。
也就是说,一旦创建了迭代器(包含 foreach 的情况),就只能调用 Itr 给的方法进行 “modify”,不然下次 checkForComodification 检查必定过不了。(modCount + 1 了,但是 expectedModCount 没有变,导致不相等)

② 从设计原则上说,迭代器一旦生成,就对应生成了游标(cursor),对父类变量(ArrayList.this.elementData)进行遍历操作,你要对父类变量进行变更,特别是触动了数据下标的变化,一定要让我来操作,不然我不知道,迭代的后续操作就不正确(安全)了。这就是“对修改封闭”(开闭原则的闭)。

解决方案:

  1. 用显式迭代器的内部方法进行集合的修改
  2. 处理成新集合,对新集合进行后续操作
  3. fori的反向操作(反向可以减少remove对index变化的影响,但是add操作还是会被影响)

网站公告

今日签到

点亮在社区的每一天
去签到