两个迭代器抛出 ConcurrentModificationException

Two iterators throw ConcurrentModificationException

我有以下代码

public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        Arrays.stream("hello how are you".split(" ")).forEach(s -> list.add(s));

        Iterator<String> it = list.iterator();
        ListIterator<String> lit = list.listIterator();

        while (it.hasNext()) {
            String s = it.next();
            if (s.startsWith("a")) {
                it.remove();
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);

        // {here}

        while (lit.hasNext()) {
            String s = lit.next();
            if (s.startsWith("a")) {
                lit.set("1111" + s);
            } else {
                System.out.println(s);
            }
        }

        System.out.println(list);
}

在这里,在遍历 Iterator 之后,我尝试遍历 ListIterator。但是代码抛出 ConcurrentModificationException。我只是在Iterator完成后才使用ListIterator进行修改,但为什么会出现此异常。

当我在 {here} 而不是顶部初始化 ListIterator 时,代码运行完美。

  1. 列表被两个线程同时修改时不是ConcurrentModificationException吗?
  2. 是否初始化迭代器,在列表上创建一个锁?如果是,那么为什么 Java 让我们在 Iterator 已经被另一个 Iterator 初始化之后让我们初始化它?

您必须在使用完第一个迭代器后加载第二个迭代器。否则第二个迭代器 "thinks" 列表没有改变,但实际上它改变了。因为列表已更改,所以第二个迭代器的反应类似于 "Wait a minute, that shouldn't be here/gone" 并抛出 ConcurrentModificationException.

它让你随时初始化迭代器。当您不更改内容时,您甚至可以接受它并且您没有得到 ConcurrentModificationException 因为没有任何更改。

A ConcurrentModificationException 可能会在您尝试使用无效的迭代器时抛出 - 无论何时您创建迭代器然后从不同的访问点修改基础集合时都会发生这种情况。在这里,lit 被初始化,然后通过 it 修改列表,因此它无效,这解释了异常。

Isn't ConcurrentModificationException thrown when the list is being modified by two threads simultaneously ?

不一定。 ConcurrentModificationException 表示在创建 Iterator 之后,列表的结构发生了变化(除了 Iterator 自己的 remove 方法)。这可能是由于多个线程使用相同的列表,或者可能是由于尝试从 for each 循环内的 ArrayList 中删除项目而不使用 Iterator.

Does initializing the iterator, create a lock on the list ?

不,没有锁。创建 Iterator 时,它会记录 ArrayListmodCount(列表状态的粗略表示,每次结构更改时都会递增)。如果迭代器检测到对 List 的 modcount 的更改不是由它自己的方法引起的,则会抛出异常。

由于在实例化和使用第二个迭代器之间对列表进行了结构更改,您从第二个迭代器中得到了异常。

why does Java let us to initialize an Iterator after it has already been initialized by another Iterator?

ArrayList 不会跟踪它创建的所有迭代器或它们的状态。这样做会使实施变得非常复杂。 modCount 方法并不完美,有点粗糙,但它很简单,并且识别出许多真正的错误。

ListIterator 抛出 ConcurrentModificationException 如果列表在创建后有修改。在您的代码中,您同时创建了 Iterator 和 ListIterator,稍后您要从导致 ConcurrentModificationException 的列表中删除某些内容。

为避免这种情况,请将您的代码更改为以下代码。您只需要在迭代器操作之后移动 ListIterator 初始化。

public static void main(String[] args) {

    List<String> list = new ArrayList<>();
    Arrays.stream("hello how are you".split(" ")).forEach(s -> list.add(s));

    Iterator<String> it = list.iterator();

    while (it.hasNext()) {
        String s = it.next();
        if (s.startsWith("a")) {
            it.remove();
        } else {
            System.out.println(s);
        }
    }

    System.out.println(list);

    ListIterator<String> lit = list.listIterator();

    while (lit.hasNext()) {
        String s = lit.next();
        if (s.startsWith("a")) {
            lit.set("1111" + s);
        } else {
            System.out.println(s);
        }
    }

    System.out.println(list);
}