为啥我会收到 ConcurrentModificationException?

Posted

技术标签:

【中文标题】为啥我会收到 ConcurrentModificationException?【英文标题】:Why do I get a ConcurrentModificationException?为什么我会收到 ConcurrentModificationException? 【发布时间】:2017-06-08 21:14:41 【问题描述】:

为什么我会在代码中的指定位置收到 ConcurrentModificationException?我无法弄清楚我做错了什么... removeMin() 方法被用于在列表pq 中找到最小值,将其删除并返回其值

import java.util.Iterator;
import java.util.LinkedList;

public class test1 

    static LinkedList<Integer> list = new LinkedList<Integer>();

    public static void main(String[] args) 
        list.add(10);
        list.add(4);
        list.add(12);
        list.add(3);
        list.add(7);

        System.out.println(removeMin());
    

    public static Integer removeMin() 
        LinkedList<Integer> pq = new LinkedList<Integer>();
        Iterator<Integer> itPQ = pq.iterator();

        // Put contents of list into pq
        for (int i = 0; i < list.size(); i++) 
            pq.add(list.removeFirst());
        

        int min = Integer.MAX_VALUE;
        int pos = 0;
        int remPos = 0;

        while (itPQ.hasNext()) 
            Integer element = itPQ.next(); // I get ConcurrentModificationException here
            if (element < min) 
                min = element;
                remPos = pos;
            
            pos++;
        

        pq.remove(remPos);
        return remPos;
    


【问题讨论】:

我的猜测是,自从你创建了Iterator 之前你开始添加额外的元素迭代器突然在它的脚下更新了它的数据,可以这么说 【参考方案1】:

一旦从中获取迭代器的集合被修改,则不应将其视为可用。 (java.util.concurrent.* 集合类放宽了这个限制。)

您首先获取pq 的迭代器,然后修改pq。一旦你修改了pq,迭代器itPQ就不再有效,所以当你尝试使用它时,你会得到一个ConcurrentModificationException。

一种解决方案是将Iterator&lt;Integer&gt; itPQ = pq.iterator(); 移动到while 循环之前的右侧。更好的方法是完全取消对 Iterator 的显式使用:

for (Integer element : pq) 

从技术上讲,for-each 循环在内部使用一个迭代器,因此无论哪种方式,只要您不尝试在循环内修改 pq,此循环才有效。

【讨论】:

赞成 foreach 方法。我个人认为这更清洁 @VGR "你首先为 pq 获取一个迭代器,然后修改 pq"。这本身不是问题。问题是没有使用迭代器自己的 add 方法。将元素添加到pq。如果使用 ListIterator 提供的 add 方法,迭代器可以毫无问题地遍历pq。但我喜欢这个解决方案。 我同意上面的说法,我已经修改了我的代码,但是现在当我删除最小值时,它会出现 IndexOutOfBoundsException @kprog 听起来像是一个不同的问题,应该作为一个新问题提出。【参考方案2】:

我运行了你的代码,结果发现有问题的行在这里:

Iterator<Integer> itPQ = pq.iterator();

这需要您的pq 人口之后进行,以便迭代器不会异步更新其数据。

通过此修改,代码运行。


现在,它确实没有正常运行。原因正如@Ishnark 在他的回答中指出的那样,每次您从列表中删除时,它都会变小,因此并非所有列表都被添加到pq

【讨论】:

【参考方案3】:

您遇到了一个问题,因为您在为pq 创建了一个迭代器之后,使用普通的.add() 方法向pq 添加了项目。当您执行 hasNext() 时,迭代器不会抱怨,因为它会看到 pq 的变化。

while (itPQ.hasNext()) 
    ...
    Integer element = itPQ.next(); --> you get exception here
    ...

但是,当您尝试遍历 pq 时,它会引发异常。根据这个post,“如果迭代器检测到一些修改没有使用它的方法(或在同一个集合上使用另一个迭代器),它不能再保证它不会在同一个元素上传递两次或跳过一个,所以它会抛出这个异常。”

【讨论】:

虽然我同意这绝对是粗略的代码,但 OP 明确指出此处不会引发错误 这与list无关。异常是由于获取itPQ,然后修改pq,然后尝试使用itPQ

以上是关于为啥我会收到 ConcurrentModificationException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到此错误

为啥我会收到这个 TypeError?

为啥我会收到序列化错误?

为啥我会收到错误消息?

知道为啥我会收到此错误吗?

为啥我会收到这种错误验证错误?