Java 中使用 addAll 超过 Set 的 ConcurrentModification 异常
我对 Java 应用程序中刚刚遇到的一个问题感到非常困惑。
当我尝试运行下面给出的代码时,Java 在“if ( this.vertexStore.get ( v ).addAll ( output ) )”行上引发 ConcurrentModificationException。
考虑到这是一个完全单线程的应用程序,而且据我所知,我实际上并没有修改我正在循环的任何内容,我觉得这很奇怪?
事实上,我能看到发生错误的唯一地方是在 addAll 方法内部,但这也不应该发生,因为我正在使用 Java 类库中的 HashMap 和 LinkedList...
private Queue<Vertex> worklist = new LinkedList<Vertex> ( );
protected Map<Vertex, Set<T>> vertexStore = new HashMap<Vertex, Set<T>> ( );
// . . .
while ( this.worklist.size ( ) > 0 ) {
Vertex vertex = this.worklist.remove ( );
Set<T> output = this.processVertice ( vertex, this.vertexStore.get ( vertex ) );
this.vertexStore.put ( vertex, output );
for ( Vertex v : vertex.edgesTo ( ) ) {
// Conveniently, addAll returns true if the set changed
if ( this.vertexStore.get ( v ).addAll ( output ) )
this.worklist.add ( v );
}
}
编辑:错误跟踪:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
at DataFlowAnalyser.process(DataFlowAnalyser.java:41) (the if line)
任何好的想法都是非常欢迎!
PS:完整源代码此处(抱歉缺少注释,代码未完成)
干杯, 乔恩
I have been completely puzzled by an issue I just came over in my Java application.
When I attempt to run the code given below, Java raises a ConcurrentModificationException on the line that says "if ( this.vertexStore.get ( v ).addAll ( output ) )".
I find this very strange considering this a completely single-threaded application, and that I am not actually modifying anything that I am looping over as far as I can tell?
In fact, the only place I can see the error occurring is inside the addAll method, but that shouldn't happen either as I'm using HashMap and LinkedList from the Java class library...
private Queue<Vertex> worklist = new LinkedList<Vertex> ( );
protected Map<Vertex, Set<T>> vertexStore = new HashMap<Vertex, Set<T>> ( );
// . . .
while ( this.worklist.size ( ) > 0 ) {
Vertex vertex = this.worklist.remove ( );
Set<T> output = this.processVertice ( vertex, this.vertexStore.get ( vertex ) );
this.vertexStore.put ( vertex, output );
for ( Vertex v : vertex.edgesTo ( ) ) {
// Conveniently, addAll returns true if the set changed
if ( this.vertexStore.get ( v ).addAll ( output ) )
this.worklist.add ( v );
}
}
EDIT: Error trace:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
at DataFlowAnalyser.process(DataFlowAnalyser.java:41) (the if line)
Any good ideas are very welcome!
PS: Full source here (sorry for lack of comments, code not completed)
Cheers,
Jon
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您正在调用 addAll 并传递对 Set 本身的引用。如果您通过代码进行调试,您将看到 this.vertexStore.get(v) 返回与输出变量引用的对象相同的对象。
通常这对于 HashSet 来说不是问题,因为如果您只是添加所有相同的元素,则 addAll 实际上不会修改 HashSet 的状态。然而,在这种情况下,您在将 HamiltonPath 的实例添加到集合后对其进行修改,这反过来会更改它们的哈希代码,并使 HashSet 认为要添加的对象与其已有的对象不同。
这是一些比我的散文更好地说明问题的代码:
You're calling addAll and passing a reference to the Set itself. If you debug through the code you'll see that this.vertexStore.get(v) returns the same object as is referenced by the output var.
Normally this wouldn't be a problem for a HashSet because addAll won't actually modify the state of a HashSet if you're just adding all the same elements. In this case however, you're modifying the instances of HamiltonPath after they've been added to the set which in turn changes their hash code and makes the HashSet think that the object being added is different than the one it already has.
Here's some code that illustrates the problem better than my prose:
你的代码会中断,因为你在迭代集合时修改了集合,而 java 不喜欢它。如果您想这样做,请使用 ConcurrentHashMap,它使用与标准映射不同类型的迭代器。
your code breaks because you are modifying a collection while you are iterating on it and java doesn't like it. If you want to do that use a ConcurrentHashMap that uses a different type of iterator than a standard map.