Java 中使用 addAll 超过 Set 的 ConcurrentModification 异常

发布于 2024-09-27 05:25:32 字数 1545 浏览 2 评论 0原文

我对 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

春花秋月 2024-10-04 05:25:32

您正在调用 addAll 并传递对 Set 本身的引用。如果您通过代码进行调试,您将看到 this.vertexStore.get(v) 返回与输出变量引用的对象相同的对象。

通常这对于 HashSet 来说不是问题,因为如果您只是添加所有相同的元素,则 addAll 实际上不会修改 HashSet 的状态。然而,在这种情况下,您在将 HamiltonPath 的实例添加到集合后对其进行修改,这反过来会更改它们的哈希代码,并使 HashSet 认为要添加的对象与其已有的对象不同。

这是一些比我的散文更好地说明问题的代码:

List<String> list1 = Arrays.asList("foo");
List<String> list2 = Arrays.asList("bar");
Set<List<String>> set = new HashSet<List<String>>();
set.add(list1);
set.add(list2);
list1.add("baz"); 
list2.add("qux");
set.addAll(set); // throws ConcurrentModificationException

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:

List<String> list1 = Arrays.asList("foo");
List<String> list2 = Arrays.asList("bar");
Set<List<String>> set = new HashSet<List<String>>();
set.add(list1);
set.add(list2);
list1.add("baz"); 
list2.add("qux");
set.addAll(set); // throws ConcurrentModificationException
谁的年少不轻狂 2024-10-04 05:25:32

你的代码会中断,因为你在迭代集合时修改了集合,而 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文