What is ConcurrentModificationException , how is it caused and how can it be prevented ? (Part 1- HashMaps)
ConcurrentModificationException occurs when a Collection is modified structurally while an iterator is already traversing the Collection.
Taking the example of a HashMap,while iterating through the entries of a HashMap, if the internal structure of the HashMap is changed by either adding an element to it or removing an element from it, a ConcurrentModificationException is caused.
Following is the output:
Initial Map:{1=Pallavi, 2=Sonal, 3=Siddhid}
Modified Map:{1=Pallavi, 2=Sonal, 3=Siddhid, 4=Nag}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926)
at java.util.HashMap$EntryIterator.next(HashMap.java:966)
at java.util.HashMap$EntryIterator.next(HashMap.java:964)
at com.explore.pallavi.NotSafeHashMap.main(NotSafeHashMap.java:29)
The following example shows how we can prevent the ConcurrentModifcation Exception using ConcurrentHashMap which uses fail-safe iterators.
Following is the output:Taking the example of a HashMap,while iterating through the entries of a HashMap, if the internal structure of the HashMap is changed by either adding an element to it or removing an element from it, a ConcurrentModificationException is caused.
What happens internally?
HashMap maintains an integer variable called modCount. This modCount is nothing but the count of times the HashMap has been modified structurally. If we add an element to the HashMap, the modCount increases. Similarly, if we remove an element from the HashMap, the modCount increases. Interestingly, when we just update an entry, there isn't any change to the modCount. This is because update to an element does not change the internal structure of the HashMap.
When we create an iterator for the Entry table of the HashMap, an expectedCount variable is created which is set to the modCount of the HashMap. This expectedCount maintains the number of structural modification counts that has happened until the time that iterator was obtained. Every time , we try to get the next element of the HashMap by calling the iterator's next() method, it checks whether the modCount is equal to the expected count. If not, it understands that the HashMap has changed internally meanwhile. Hence, to prevent future problems, it just fails the code by throwing the ConcurrentModification Exception.
Such an iterator is said to be fail-fast.
However, when we use the ConcurrentHashMap instead of the HashMap, we don't get this exception even though the structure of the Map is changed since an iterator was obtained over its Entry table. This is because the Iterators in the ConcurrentHashMap are designed to be fail-safe. In case of fail-safe iteration, the comparison between modCount is not done while returning the next entry. But it doesn't guarantee that the iterator is going to see the modfication done to the Map.
The following example shows such an example of the ConcurrentModifcation Exception with fail-fast iterators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package in.blogspot.pallavisonal; import java.util.HashMap; import java.util.Iterator; public class NotSafeHashMap { private HashMap<Integer,String> myMap; public NotSafeHashMap(HashMap<Integer,String> myMap){ this.myMap=myMap; } public static void main(String[] args) { HashMap<Integer,String> map = new HashMap<>(); map.put(1,"Pallavi"); map.put(2,"Sonal"); map.put(3,"Siddhid"); NotSafeHashMap nsMap = new NotSafeHashMap(map); System.out.println("Initial Map:"+map); Iterator<?> iter = nsMap.myMap.entrySet().iterator(); if(iter.hasNext()){ iter.next(); } map.put(4,"Nag"); System.out.println("Modified Map:"+map); while(iter.hasNext()){ System.out.println(iter.next()); //fails here } } } |
Following is the output:
Initial Map:{1=Pallavi, 2=Sonal, 3=Siddhid}
Modified Map:{1=Pallavi, 2=Sonal, 3=Siddhid, 4=Nag}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926)
at java.util.HashMap$EntryIterator.next(HashMap.java:966)
at java.util.HashMap$EntryIterator.next(HashMap.java:964)
at com.explore.pallavi.NotSafeHashMap.main(NotSafeHashMap.java:29)
The following example shows how we can prevent the ConcurrentModifcation Exception using ConcurrentHashMap which uses fail-safe iterators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package in.blogspot.pallavisonal; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; public class SafeHashMap { private ConcurrentHashMap<Integer,String> myMap; public SafeHashMap(ConcurrentHashMap<Integer,String> myMap){ this.myMap=myMap; } public static void main(String[] args) { ConcurrentHashMap<Integer,String> map = new ConcurrentHashMap<>(); map.put(1,"Pallavi"); map.put(2,"Sonal"); map.put(3,"Siddhid"); SafeHashMap nsMap = new SafeHashMap(map); System.out.println("Initial Map:"+map); Iterator<?> iter = nsMap.myMap.entrySet().iterator(); if(iter.hasNext()){ iter.next(); } map.remove(3); System.out.println("Modified Map:"+map); while(iter.hasNext()){ iter.next(); } } } |
Initial Map:{2=Sonal, 1=Pallavi, 3=Siddhid}
Modified Map:{2=Sonal, 1=Pallavi}
Comments