getting concurrentmodification error while looping on hashmap

Viewed 852

I have the following error:

Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1584)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1607)
at Server$1.run(Server.java:149)
at java.base/java.lang.Thread.run(Thread.java:832)

Reffering to this code:

    for (Session key : sessions.keySet()) {
                        if (key.getPort2() != port && key.getPort1() != port) { // change later to ip
                            System.out.println("2nd time init 2 client");

                            session.setIp2(ip);
                            session.setPort2(port);

                            sessionID++;

                            sessions.put(session, sessionID);

                            // reset session
                            session = null;

                        }
                    }

Could you explain why this happens?

3 Answers

HashMap and the collections returned by its entrySey, keySet or values methods, do not allow changes (insertion, deletion) while iterating over it. You can create a temporary list to loop over while still changing the original map:

for (Session key : new ArrayList<>(sessions.keySet())) {
    ...
    sessions.put(session, sessionID);
    ...
}

You can also create a temporary list to store the elements to be inserted (after the loop).


Not the question, but not sure what that part is for:

// reset session
session = null;

kind of dangerous -> NullPointerException on following iterations (assuming if block is executed)

You modify the map while you iterate over its content, which forbidden.

Almost all collection classes forbid this for a good reason. Lets assume you have a collection which contains

a b c g h i j k l

Now you iterate over the elements and while processing element k you insert d. Would you expect that d is skipped because you are already bejond that position? What if you are at position b and insert an m? Would you expect that m will be processed? Or would you expect that new elements will not be processed because they did not exist at the time when you started iterating?

If Java would allow that, people would complain about unexpected behavior. And that is the reason why Java does not allow it. It is one major goal of Java to avoid unexpected behavior, compared to older programming languages.

To solve this, create a second temporary empty map before you start iterating. Then inside your loop you can put new elements into that temporary map. Then finally combine them:

Map<String, String> tmp=new HashMap<>();
for (Session key : sessions.keySet()) {
   ...
   tmp.put(session, sessionID);
}
sessions.putAll(tmp);

This way it is clear what happens, no unexpected behavior.

java.util.HashMap is not thread safe if you are looping through the HashMap elements and at the same time if you try to modify it JVM will through ConcurrentModificationException. You can either iterate the HashMap element by Iterator or use ConcurrentHashMap instead.

Related