两个迭代器抛出了ConcurrentModificationException异常

3
我有以下代码。
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);
}

在这里,我通过迭代器进行迭代后,尝试遍历ListIterator。但代码抛出了ConcurrentModificationException异常。我在Iterator完成后才使用ListIterator进行修改,为什么会出现此异常。
当我在{here}处初始化ListIterator而不是顶部时,代码运行得非常完美。
1、ConcurrentModificationException不是在两个线程同时修改列表时抛出的吗?
2、初始化迭代器是否会在列表上创建锁定? 如果是的话,为什么Java允许我们在另一个Iterator已经被初始化后初始化Iterator?
4个回答

2
当两个线程同时修改列表时,是否会抛出ConcurrentModificationException异常?
不一定。ConcurrentModificationException表示在创建Iterator后(除了Iterator自己的remove方法),列表已经发生了结构性更改。这可能是由于多个线程使用同一个列表,也可能是由于在不使用Iterator的情况下尝试从ArrayList中删除项目。
初始化迭代器是否会锁定列表?
不会有锁。当创建Iterator时,它记录ArrayList的modCount(每次结构性更改时增加的列表状态的粗略表示)。如果迭代器检测到List的modcount发生了变化,而这种变化不是由它自己的方法引起的,则抛出异常。
第二个迭代器抛出异常是因为在第二个迭代器被实例化和使用之间对列表进行了结构性更改。
为什么Java允许我们在另一个Iterator初始化之后初始化Iterator?
ArrayList不跟踪它创建的所有迭代器或它们的状态。这样做会极大地复杂化实现。modCount方法并不完美,有点粗糙,但它简单并且可以识别许多真正的错误。

这是否意味着没有两个迭代器可以在同一个列表上操作? - v1shnu
你可以同时拥有两个迭代器遍历同一个列表。如果它们不添加或删除元素,就不会抛出异常。 - Paul Boddington
@PaulBoddington 为什么我们不能在同时迭代同一列表时执行更新操作?迭代器在迭代时是否保留它们自己的副本,并在所有迭代结束后更新最终列表,还是在每次迭代时更新原始列表? - Shubhendu Pramanik
@ShubhenduPramanik 不,它们不会保留副本,它们只会跟踪单个 int 值的变化,当元素被添加或删除时。 - Paul Boddington

1

在使用第一个迭代器后,您必须加载第二个迭代器。否则,第二个迭代器“认为”列表没有被更改,但实际上已经更改了。因为列表已经更改,第二个迭代器会像“等一下,这里/那里不应该有/没有”的反应,并抛出ConcurrentModificationException异常。

它允许您随时初始化迭代器。当您不更改内容时,您甚至可能会很好地使用它,并且不会收到ConcurrentModificationException异常,因为没有任何更改。


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

0

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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接