Thursday, February 26, 2015

Simple way to avoid ConcurrentModificationException

    Some time ago I spotted ConcurrentModificationExcepion in the logs. I decided to take a closer look at that in order to get rid of the problem.
After some simplification the general sense of the code was like that:
import java.util.HashMap;
import java.util.Map;
 
public class Main {
    public static void main(String... args) throws Exception {
     Map<String, String> map = new HashMap<>();
     map.put("Check", "Me");
     map.put("CheckThis", "Out");
     
     for (String key : map.keySet()) {
      map.remove(key);
     }
    }
}
And the result:
Exception in thread "main" java.util.ConcurrentModificationException
 at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429)
 at java.util.HashMap$KeyIterator.next(HashMap.java:1453)
 at test.Main.main(Main.java:12)
The answer for the problem can be found in JDK's Javadoc. The description of Map.keySet method says:
 * Returns a {@link Set} view of the keys contained in this map.
 * The set is backed by the map, so changes to the map are
 * reflected in the set, and vice-versa.
This is why we have that nasty exception. Is there any cure? Well, yes. Some folks on the Internet propose to use Iterator. It can do the trick but it is ugly. The better option is to use:
public static void main(String... args) throws Exception {
     Map<String, String> map = new HashMap<>();
     map.put("Check", "Me");
     map.put("CheckThis", "Out");
     
     for (String key : new HashSet<>(map.keySet())) {
      map.remove(key);
     }
}
Thanks to that we create a copy of Set<String> which is not backed by the map eligible for modification. Simple yet elegant and effective if you do not deal with huge sets.

2 comments :