Exception in thread ... java.util.ConcurrentModificationException
at java.util.Hashtable$Enumerator.next(Unknown Source)
at ...
A quick look at the API documentation of ConcurrentModificationException says:
"This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it..."Now this may not be very useful for newbies. They may be left wondering about multi-threading, concurrent access etc. Given below is a sample code which is single threaded, yet, it fails with ConcurrentModifcationAccess. In this code I am trying to remove keys that end with digit "5" from a Map:
public class MapTest {
public static void main(String[] args) {
//create a sample Map
Map<String, String> map = new Hashtable<String,String>();
map.put("key1", "value100");
map.put("key2", "value200");
map.put("key3", "value300");
map.put("key4", "value400");
map.put("key5", "value500");
map.put("key55", "value550");
map.put("key6", "value600");
System.out.println("mappings before: "+map);
//retrieve the set of Keys in the map
Set<String> keySet = map.keySet();
//iterate over keys and remove
// those which ends with "5"
for(String key : keySet) {
if(key.endsWith("5" )) {
keySet.remove(key);
}
}
System.out.println("mappings after: "+map);
}
}
When you run this code it will throw concurrent modification exception. Now try the following sample. Changed code is highlighted.
This code runs fine. Why?
public class MapTest {
public static void main(String[] args) {
//create a sample Map
Map<String, String> map = new Hashtable<String,String>();
map.put("key1", "value100");
map.put("key2", "value200");
map.put("key3", "value300");
map.put("key4", "value400");
map.put("key5", "value500");
map.put("key55", "value550");
map.put("key6", "value600");
System.out.println("mappings before: "+map);
//retrieve the set of Keys in the map
Iterator<String> keySetItr = map.keySet().iterator();
while( keySetItr.hasNext()) {
String key = keySetItr.next();
if(key.endsWith("5" )) {
keySetItr.remove();
}
}
System.out.println("mappings after: "+map);
}
}
The evil is in the code:
for(String key : keySet) {
...
keySet.remove(key);
The for(...) actually opens an iterator internally and when we remove the key from KeySet this iterator logic breaks, throwing the ConcurrentModificationException. To avoid this one should use the second example given above, where same Iterator (keySetItr) is used both for iteration and removing the keys.
good tip... thank you!
ReplyDeletenice info!
ReplyDeleteThat was driving me mad! Thanks for the solution mate!
ReplyDeleteGreat tip man. More power!
ReplyDeleteThank you very much. This was such a difficult to understand bug.. such as elegant solution though, use the given remove feature in the iterator instead! Of course... duh !!
ReplyDeleteI have another question here.
ReplyDeleteIf I have multi threaded application and one thread synchronized on the map and then open an iterator while other thread trying to modify the map (this thread is not synchronized), will we get the same error ?
Actually I am getting this error in this scenario and wondering how it is possible ?
Excellent! Gracias amigo
ReplyDeleteI always wondered how to get around this Exception. Thanks a lot!
ReplyDeleteThanks a lot, you just saved my morning :)
ReplyDeletereally useful info! thank you!!
ReplyDeletenice tip. Thanks much.
ReplyDeleteChe Dio ti benedica!
ReplyDeleteDio benu vin! DANKON
God bless you