Search code examples
javacollectionshashmapiterationconcurrentmodification

Avoiding ConcurrentModificationException while modifying multiple Maps


I have class containing multiple HashMaps. The values in these HashMaps are the same but the keys are different. I have to remove the same element from all Maps that contain it. The method that removes these elements takes a Collection as argument, iterates over it and removes elements from multiple HashMaps. Here is the code:

private Map<Position, Place> map1 = new HashMap<Position, Place>();
private Map<String, List<Place>> map2 = new HashMap<String, List<Place>>();
private Map<Category, List<Place>> map3 = new HashMap<Category, List<Place>>();

public void removePlaces2(Collection<Place> places) {
    Iterator<Place> iter = places.iterator();
    while (iter.hasNext()) {
        Place p = iter.next();
        Position pos = p.getPosition();
        String name = p.getName();
        Category cat = p.getCategory();
        map1.remove(pos);
        List<Place> list1 = map2.get(name);
        list1.remove(p);
        if (list1.isEmpty()) {
            map2.remove(name);
        }
        if (cat != null) {
            List<Place> list2 = map3.get(cat);
            list2.remove(p);
        }
        this.remove(p);
        modified = true;
    }
    revalidate();
}

The method throws an ConcurrentModificationException at the line Place p = iter.next();. (But not every time.) I'm at a loss as to how to avoid it. If I use iter.remove(p) that will only remove the element from the argument to the method: Collection<Place> places. And this is not what I want. The question is how can I avoid this exception while removing the element from multiple maps? Notice: I am not iterating over the maps from which the element is to be removed.


Solution

  • If you are passing map1, map2, or map3 as an argument to removePlaces2(), create a copy instead:

    removePlaces2(new LinkedList<Place>(map1.values()));
    

    If the exception persists try using thread safe versions of the maps:

    private Map<Position, Place> map1 = Collections.synchronizedMap(new HashMap<Position, Place>());
    private Map<String, List<Place>> map2 = Collections.synchronizedMap(new HashMap<String, List<Place>>());
    private Map<Category, List<Place>> map3 = Collections.synchronizedMap(new HashMap<Category, List<Place>>());