ConcurrentModificationException when using iterator to remove entry

Viewed 1062

I have a simple piece of code that loops through a map, checks a condition for each entry, and executes a method on the entry if that condition is true. After that the entry is removed from the map. To delete an entry from the map I use an Iterator to avoid ConcurrentModificationException's.

Except my code does throw an exception, at the it.remove() line:

Caused by: java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.remove(Unknown Source) ~[?:1.8.0_161]
    at package.Class.method(Class.java:34) ~[Class.class:?]

After a long search I can't find a way to fix this, all answers suggest using the Iterator.remove() method, but I'm already using it. The documentation for Map.entrySet() clearly specifies that it is possible to remove elements from the set using the Iterator.remove() method.

Any help would be greatly appreciated.

My code:

Iterator<Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
    Entry<K, V> en = it.next();

    if (en.getValue().shouldRun()) {
        EventQueue.invokeLater(()->updateSomeGui(en.getKey())); //the map is in no way modified in this method
        en.getValue().run();
        it.remove(); //line 34
    }
}
3 Answers

If you cannot change HashMap to ConcurrentHashMap you can use another approach to your code.

You can create a list of entries containing the entries that you want to delete and then iterate over them and remove them from the original map.

e.g.

    HashMap<String, String> map = new HashMap<>();
    map.put("1", "a1");
    map.put("2", "a2");
    map.put("3", "a3");
    map.put("4", "a4");
    map.put("5", "a5");
    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
    List<Map.Entry<String, String>> entries = new ArrayList<>();

    while (iterator.hasNext()) {
        Map.Entry<String, String> next = iterator.next();
        if (next.getKey().equals("2")) {
            /* instead of remove
            iterator.remove();
            */
            entries.add(next);
        }
    }

    for (Map.Entry<String, String> entry: entries) {
        map.remove(entry.getKey());
    }

For such purposes you should use the collection views a map exposes:

keySet() lets you iterate over keys. That won't help you, as keys are usually immutable.

values() is what you need if you just want to access the map values. If they are mutable objects, you can change directly, no need to put them back into the map.

entrySet() the most powerful version, lets you change an entry's value directly.

Example: convert the values of all keys that contain an upperscore to uppercase

for(Map.Entry<String, String> entry:map.entrySet()){
    if(entry.getKey().contains("_"))
        entry.setValue(entry.getValue().toUpperCase());
}

Actually, if you just want to edit the value objects, do it using the values collection. I assume your map is of type <String, Object>:

for(Object o: map.values()){
    if(o instanceof MyBean){
        ((Mybean)o).doStuff();
    }
}
Related