尝试迭代HashMap

时间:2017-08-17 05:09:22

标签: java hashmap iteration nested-lists

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

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

中的位置

我试图在我的更新函数中迭代我的世界中的所有块,因为每个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转换为其他人使用我的引擎可以挂钩的事件。

3 个答案:

答案 0 :(得分:2)

此处ConcurrentModificationException表示在迭代期间修改了地图而不使用迭代器。

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

答案 1 :(得分:0)

嗯,这段代码导致了麻烦

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

使用Iterator并调用更新

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

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

答案 2 :(得分:0)

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

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