How to avoid ConcurrentModificationException when iterating over a map and changing values?

Two options:

Option 1

The current code I've inherited removes the given entry, and adds it back in after making some changes to the POJO.

Are you changing the reference to the POJO? E.g., so the entry points to something else entirely? Because if not, there's no need to remove it from the map at all, you can just change it.

Option 2

If you do need to actually change the reference to the POJO (e.g., the value of the entry), you can still do that in place by iterating over the Map.Entry instances from entrySet(). You can use setValue on the entry, which doesn't modify what you're iterating over.

Example:

Map<String,String>                  map;
Map.Entry<String,String>            entry;
Iterator<Map.Entry<String,String>>  it;

// Create the map
map = new HashMap<String,String>();
map.put("one", "uno");
map.put("two", "due");
map.put("three", "tre");

// Iterate through the entries, changing one of them
it = map.entrySet().iterator();
while (it.hasNext())
{
    entry = it.next();
    System.out.println("Visiting " + entry.getKey());
    if (entry.getKey().equals("two"))
    {
        System.out.println("Modifying it");
        entry.setValue("DUE");
    }
}

// Show the result
it = map.entrySet().iterator();
while (it.hasNext())
{
    entry = it.next();
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

The output (in no particular order) is:

Visiting two
Modifying it
Visiting one
Visiting three
two=DUE
one=uno
three=tre

...without any modification exception. You will probably want to synchronize this in case something else is also looking at / mucking with that entry.


Iterating over a Map and adding entries at the same time will result in a ConcurrentModificationException for most Map classes. And for the Map classes that don't (e.g. ConcurrentHashMap) there is no guarantee that an iteration will visit all entries.

Depending on exactly what it is you are doing, you may be able to do the following while iterating:

  • use the Iterator.remove() method to remove the current entry, or
  • use the Map.Entry.setValue() method to modify the current entry's value.

For other types of change, you may need to:

  • create a new Map from the entries in the current Map, or
  • build a separate data structure containing changes to be made, then applied to the Map.

And finally, the Google Collections and Apache Commons Collections libraries have utility classes for "transforming" maps.


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

Tags:

Java

Loops

Map