简体   繁体   中英

Receiving ConcurrentModificationException when attempting to iterate through HashMap

I am having issues with a project I am working on. There is a main loop that runs the program which calls the update() method every frame.

I have an object that is a World which contains a HashMap<ChunkPos, Chunk> where a ChunkPos is a data structure that has an xyz position which represents a Chunk 's position in the World

I am attempting to iterate through all the Chunks in my the world in my update function, as each Chunk has it's own ArrayList of GameObject that has all of the objects inside this chunk of the world.

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();
        }
    }
}

However, when I run the program, I am receiving a ConcurrentModificationException at this line:

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

I've tried multiple different ways of iterating through the HashMap, but each time I end up with this same error. What am I doing wrong?

Here is the full StackTrace if it helps:

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)

Just wanted to make a note here: I eventually ended up implementing a WorldChangeScheduler that would schedule changes to be made to the world once it has iterated through the whole world and updated it. This makes it more robust and has the added benefit that I can later go back and turn these WorldChanges into events that other people using my engine can hook into.

Here ConcurrentModificationException means that the map was modified during iteration without using the iterator.

Either obj.update(); modifies the world.getChunkMap() map or you have another thread that concurrently adds or removes an element of this same map during the iteration of the current void update() method.
Because in the shown code there is not any modification of the map.

Well, this code is causing the trouble

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

Use an Iterator and call update

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

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

HashMap is not thread-safe, so iterators fail fast when structural modifications happen by other threads, while iterating.

By replacing your Map implementation with ConcurrentHashMap , you will prevent such issues, but locking will apply on the update operations, which will affect their performance. You can configure concurrency level, thought, using the concurrencyLevel parameter of the ConcurrentHashMap, if a predictable number of threads is expected to update this Map concurrently.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM