尝试迭代HashMap时接收ConcurrentModificationException

问题描述 投票:3回答:3

我正在处理我正在进行的项目的问题。有一个主循环运行程序,每个帧调用update()方法。

我有一个World的对象,其中包含HashMap<ChunkPos, Chunk>,其中ChunkPos是一个数据结构,其xyz位置代表ChunkWorld中的位置

我试图在我的更新函数中遍历我的世界中的所有块,因为每个Chunk都有自己的GameObject的ArrayList,其中包含了这个世界中的所有对象。

public void update() {
    HashMap<ChunkPos, Chunk> chunkMap = world.getChunkMap();
    Iterator<Map.Entry<ChunkPos, Chunk>> it = chunkMap.entrySet().iterator();
    while(it.hasNext()) {
        Map.Entry<ChunkPos, Chunk> item = it.next();
        ArrayList<GameObject> chunkObjects = item.getValue().getObjects();
        for(GameObject obj : chunkObjects) {
            obj.update();
        }
    }
}

但是,当我运行该程序时,我在此行收到一个ConcurrentModificationException

Map.Entry<ChunkPos, Chunk> item = it.next();

我已经尝试了多种不同的迭代HashMap的方法,但每次我都会遇到同样的错误。我究竟做错了什么?

如果它有帮助,这是完整的StackTrace:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
at java.util.HashMap$EntryIterator.next(HashMap.java:1463)
at java.util.HashMap$EntryIterator.next(HashMap.java:1461)
at com.anzanama.lwjgl3d.Game.Game.update(Game.java:40)
at com.anzanama.lwjgl3d.Game.Game.loop(Game.java:31)
at com.anzanama.lwjgl3d.Main.main(Main.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

只是想在这里做一个说明:我最终最终实现了一个WorldChangeScheduler,它可以安排在世界迭代并更新后对世界进行更改。这使得它更加强大,并且具有额外的好处,我可以稍后返回并将这些WorldChanges转换为其他人使用我的引擎可以挂钩的事件。

java hashmap iteration nested-lists
3个回答
2
投票

这里ConcurrentModificationException意味着在迭代期间修改了地图而不使用迭代器。

要么obj.update();修改world.getChunkMap()地图,要么你有另一个线程在当前void update()方法的迭代过程中同时添加或删除同一个地图的元素。 因为在显示的代码中没有对地图进行任何修改。


0
投票

好吧,这段代码造成了麻烦

ArrayList<GameObject> chunkObjects = item.getValue().getObjects();
for(GameObject obj : chunkObjects) {
    obj.update();
}

使用迭代器并调用更新

Iterator<GameObject> iter = item.getValue().getObjects().iterator();

while (iter.hasNext()) {
    GameObject obj = iter.next();
    obj.update();
}

0
投票

HashMap不是线程安全的,因此在迭代时,当其他线程发生结构修改时,迭代器会快速失败。

通过使用ConcurrentHashMap替换Map实现,您将防止此类问题,但锁定将应用于更新操作,这将影响其性能。如果期望可预测数量的线程同时更新此Map,则可以使用ConcurrentHashMap的concurrencyLevel参数配置并发级别思想。

© www.soinside.com 2019 - 2024. All rights reserved.