使用PriorityQueue时出现ConcurrentModificationException

时间:2014-04-05 05:26:50

标签: java collections

我正在使用for-each循环迭代PriorityQueue并尝试轮询它。

为什么我会java.util.ConcurrentModificationException?我在哪里修改“concurrently”,因为例外名称暗示?是否在内部使用poll()指定自己的修改?请解释一下。

  

段:

String[] sa = {">ff<", "> f<", ">f <", ">FF<"};
        //This code demonstrates that white spaces come before capital letters and 
        //capital letters come before the small letters in natural ordering
        PriorityQueue<String> pq = new PriorityQueue<String>();
        for (String str : sa) {
            pq.offer(str);
        }
        System.out.println(pq);
        for (String str : pq) {
            System.out.print(pq.poll() + " ");
        }
}

修改

根据我从下面给出的解释中理解,每次poll()方法调用都会导致PriorityQueue对象的大小发生变化。因此,在迭代并尝试轮询(varying the size constantly)时抛出异常是有意义的。因此,pq对象抛出的异常是用词不当。我的理解是否正确?

5 个答案:

答案 0 :(得分:4)

您正在与迭代(pq.poll())同时修改它(for (String str : pq))。

尽管顾名思义,但并发并不意味着多线程意义上的并发性。它只是意味着“与...同时”。具体来说,如果您在迭代它们的过程中修改了大多数集合类 - 就像在这种情况下那样 - 它们会抛出ConcurrentModificationException。请注意,不要所有吗?特别是那些专为多线程访问而设计的那些。但是大多数基本的,例如PriorityQueue,都是。

答案 1 :(得分:3)

像下面一样迭代:

while (!pq.isEmpty()) {
        System.out.print(pq.poll() + " ");
    }

然后你不会得到ConcurrentModificationException。

答案 2 :(得分:3)

  

为什么我会收到java.util.ConcurrentModificationException?。

因为您在迭代时修改集合。

  

我在哪里修改'并发',如异常名称所示?

您在致电poll()时正在修改它。该调用删除队列的第一个元素(如果有的话)......这是对队列的修改。

  

在内部使用poll()吗?

为自己分配修改      

我在谈论(String str:pq)行。 pq中的每个元素都在一次迭代中分配给str吗?

没有。它内部没有使用poll

这没有意义! for循环创建一个迭代器,然后在其上调用hasNext()next()。但是这些调用不允许修改底层集合。 poll方法会修改基础集合,因此无法使用明确。 (但随意查找/阅读PriorityQueue的源代码。)


我应该注意,当我(和其他答案)讨论迭代时,我们指的是Collection对象提供的Iterator对象的显式或隐式使用。当您将“增强型”for循环应用于集合时,您隐式创建并使用Iterator

相比之下,@ Shekhar的解决方案并未使用Iterator,因此CME的代码没有问题。

  

我还没有得到的一点是为什么for-each创建了一个Iterator?

这是因为Java语言规范它确实如此!这是如何 Java为这种对象实现“每次”迭代。您可以在规范中阅读所有相关信息 - JLS 14.14.2

(但请注意,当您将“增强型”for循环应用于数组时,不会创建迭代器。)

答案 3 :(得分:1)

来自ConcurrentModificationException javadocs

请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出违反对象合同的一系列方法调用,则该对象可能会抛出此异常。例如,如果线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。

答案 4 :(得分:1)

CME的原因是,#34;您在迭代循环时同时修改集合(删除,添加或更新)。在您的情况下,您正在调用正在更新集合的poll()方法。请参阅PriorityQueue.java的源代码以获取poll()方法&#34;

所以你最好的选择是在遍历收集时使用Iterator。这将阻止CME