3

Fun question here. Why does the first version of this throw a concurrent modification error, while the second does not. Should this happen?

Map<String,Integer> map = new HashMap<>();    
... // Populate the map
for(String key : map.keySet()){
    if(map.get(key) < 50){
        map.remove(key);
    }
}

Map<String,Integer> map = new HashMap<>();    
... // Populate the map
for(String key : new ArrayList<String>(map.keySet())){
    if(map.get(key) < 50){
        map.remove(key);
    }
}
2
  • Probably because the second uses Java.util.iterator, that has remove method created for that Commented Mar 30, 2017 at 22:28
  • 1
    With the first one you are basically creating an Iterator and if you have an Iterator for some type of Set, then accesses to the Set are invalid. I'm not sure if I'm remembering this correctly Commented Mar 30, 2017 at 22:28

3 Answers 3

4

The first example throws an exception because you modify the map while you are iterating over it. That is expected.

In the second example, you create an ArrayList containing all the strings in the map. Here you iterate over that newly created ArrayList, so your second example don't throw an example because you iterate over the ArrayList, not over the map

Sign up to request clarification or add additional context in comments.

Comments

0

This

for(String key : map.keySet()){
    if(map.get(key) < 50){
        map.remove(key);
    }
}

will always throw a ConcurrentModificationException because you remove items while you iterate. What you need is an Iterator which has the remove operation:

for(Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); it.hasNext(); ) {
    Map.Entry<String, Integer> entry = it.next();
    if(entry.getValue() < 50) {
      it.remove();
    }
}

There is also the ConcurrentHashMap which supports concurrent operations and only locks buckets if you need it.

Comments

0

In the first case you get a concurrentModificationException because there's a modification count associated with iteration in terms of internal implementation. If the modification count changes during iteration a concurrentModificaitonException is thrown.

The solution is to use iterator.remove() instead of removing an element directly from the map.

In the second case, you are iterating not the map but a different collection while removing an element from the map. In this case, the modification count never changes during iteration because you are iterating a different collection.

Also, in a multithreaded environment always use synchronized on a collection that represents shared mutable state in a class before iterating it otherwise you can get a concurrentModificationException .

In a multithreaded environment, your second solution would not be correct because you haven't synchronized the statement where you transfer the keyset of the original map to a new collection. So there's potential to get a concurrentModificationException. Use a ConcurrentHashMap in a multithreaded environment while knowing that not every operation or set of operations on a ConcurrentHashMap is threadsafe by default.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.