我正在使用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
对象抛出的异常是用词不当。我的理解是否正确?
答案 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