When you operate a java collection object use iterator, you may always meet java.util.ConcurrentModificationException error. It is because the java collection object has been changed while others still use the method iterator.next() to loop in it. Below is just an example code that can throw concurrent modification exceptions.
1. Java Concurrent Modification Exception Examples.
- When you execute the below java code, you may see ConcurrentModificationException like below.
Current phone is Apple Current phone is Google Current phone is Microsoft Exception in thread "main" java.util.ConcurrentHodificationException at java.uti1.ArrayList$Itr.checkForComodification(Unknown Source) at java.uti1.ArrayList$Itr.next(Unknown Source) at com.dev2qa.java.basic.collection.TestConcurrentModificationException.testListCMException(TestConcurrentModificationException.java:46) at com.dev2qa.java.basic.collection.TestConcurrentModificationException.main(TestC0ncurrentModificationException.java:19)
- ConcurrentModificationException occurred after removing one object in the java List.
public static void testListCMException() { /* Initiate a ArrayList object. */ List<String> phoneList = new ArrayList<String>(); phoneList.add("Apple"); phoneList.add("Google"); phoneList.add("Microsoft"); phoneList.add("Huawei"); phoneList.add("Xiao Mi"); phoneList.add("Vivo"); /* Get the object's iterator. */ Iterator<String> it = phoneList.iterator(); /* Iterate the object. */ while(it.hasNext()) { String phone = it.next(); System.out.println("Current phone is " + phone); /* Remove the object.*/ if(phone.equalsIgnoreCase("microsoft")) { /* ArrayList changed after remove, so it.next() will throw ConcurrentModificationException. */ phoneList.remove(phone); } } }
- ConcurrentModificationException occurred after add another key-value pair in java Map.
public static void testMapCMException() { /* Initiate a Map object. */ Map<String, String> osMap = new HashMap<String, String>(); osMap.put("android", "android"); osMap.put("ios", "ios"); osMap.put("macos", "macos"); osMap.put("windows", "windows"); osMap.put("linux", "linux"); osMap.put("unix", "unix"); /* Get the map object's values collection. */ Collection<String> vCollection = osMap.values(); /* Get vCollection' iterator. */ Iterator<String> it = osMap.values().iterator(); /* Iterate vCollection. */ while(it.hasNext()) { String os = it.next(); System.out.println("OS is " + os); /* Add new object in the map.*/ if(os.equalsIgnoreCase("ios")) { /* After add, collection changed, so it.next() will throw ConcurrentModificationException. */ osMap.put("redhat", "redhat"); } } }
- ConcurrentModificationException occurred with subList after original List change.
public void testSubListCMException() { List<String> fruitList = new ArrayList<>(); fruitList.add("Apple"); fruitList.add("Orange"); fruitList.add("Banana"); fruitList.add("Pear"); fruitList.add("hawthorn"); List<String> subFruitList = fruitList.subList(0, 2); System.out.println(fruitList + " , " + subFruitList); fruitList.set(1, "Watermelon "); System.out.println(fruitList + " , " + subFruitList); // Add another fruit. fruitList.add("cherry"); /* Remove comment below code, get the sub list again after original list change can avoid ConcurrentModificationException. */ //subFruitList = fruitList.subList(0, 2); //Below code will throw ConcurrentModificationException, because original list fruitList changed. System.out.println(fruitList +" , "+subFruitList); }
2. How To Fix Java ConcurrentModificationException.
- Jdk1.5 or higher provides Concurrent Collection classes that you can use to avoid this exception. CopyOnWriteArrayList and ConcurrentHashMap are two example class.
- CopyOnWriteArrayList Example.
public static void testListWithoutCMException() { /* Initiate a CopyOnWriteArrayList object. */ List<String> languageList = new CopyOnWriteArrayList<String>(); languageList.add("java"); languageList.add("c++"); languageList.add("python"); languageList.add("perl"); languageList.add("javascript"); languageList.add("go"); System.out.println("Strings before remove. "); /* Print each string in the list before remove.*/ for(String codingLanguage : languageList) { System.out.println(codingLanguage); } /* Get the object's iterator. */ Iterator<String> it = languageList.iterator(); /* Iterate the object. */ while(it.hasNext()) { String codingLanguage = it.next(); /* Remove the object.*/ if(codingLanguage.equalsIgnoreCase("perl")) { languageList.remove(codingLanguage); languageList.add("scala"); } } System.out.println(""); System.out.println("Strings after remove. "); /* Print each string in the list after remove.*/ for(String codingLanguage : languageList) { System.out.println(codingLanguage); } }
Below is the output of the above source code.
List strings before remove java C++ python perl javascript go List strings after remove. java C++ python javascript go scala
- ConcurrentHashMap Example.
public static void testMapWithoutCMException() { /* Initiate a ConcurrentHashMap object. */ Map<String, String> dbMap = new ConcurrentHashMap<String, String>(); dbMap.put("oracle", "oracle"); dbMap.put("mysql", "mysql"); dbMap.put("db2", "db2"); dbMap.put("sql server", "sql server"); dbMap.put("hadoop", "hadoop"); dbMap.put("mongodb", "mongodb"); /* Get the map object's values collection. */ Collection<String> vCollection = dbMap.values(); /* Get vCollection' iterator. */ Iterator<String> it = dbMap.values().iterator(); /* Iterate vCollection. */ while(it.hasNext()) { String db = it.next(); /* Add new object in the map.*/ if(db.equalsIgnoreCase("db2")) { dbMap.put("Big Table", "Big Table"); } } System.out.println("Map data after add. "); Set<String> dbKeySet = dbMap.keySet(); Iterator<String> dbKeySetIt = dbKeySet.iterator(); while(dbKeySetIt.hasNext()) { String db = dbKeySetIt.next(); System.out.println("Database is " + db); } }
Output
Map data after add. Database is oracle Database is Big Table Database is db2 Database is mysql Database is hadoop Database is mongodb Database is sql server
- To fix the original List changed caused subList ConcurrentModificationException, you just need to get the subList again as below example source code.
public void testSubListCMException() { List<String> fruitList = new ArrayList<>(); fruitList.add("Apple"); fruitList.add("Orange"); fruitList.add("Banana"); fruitList.add("Pear"); fruitList.add("hawthorn"); List<String> subFruitList = fruitList.subList(0, 2); System.out.println(fruitList + " , " + subFruitList); fruitList.set(1, "Watermelon "); System.out.println(fruitList + " , " + subFruitList); // Add another fruit in the list. fruitList.add("cherry"); /* get the sub list again after original list change can avoid ConcurrentModificationException. */ subFruitList = fruitList.subList(0, 2); //Below code will throw ConcurrentModificationException, because original list fruitList changed. System.out.println(fruitList +" , "+subFruitList); }
- Use for loop instead of the iterator.
public void testListWithoutCMExceptionUseForLoop() { /* Initiate a Arraylist object. */ List<String> ballList = new ArrayList<String>(); ballList.add("football"); ballList.add("basketball"); ballList.add("volleyball"); System.out.println("List strings before remove. "); /* Print each string in the list before remove.*/ for(String ball : ballList) { System.out.println(ball); } int size = ballList.size(); for(int i = 0;i<size;i++) { String ball = ballList.get(i); if("football".equalsIgnoreCase(ball)) { ballList.add("table tennis"); // After add, should recalculate the size. size = ballList.size(); } if("basketball".equalsIgnoreCase(ball)) { ballList.remove(ball); // After remove, should recalculate the size and i--. i--; size = ballList.size(); } } System.out.println(""); System.out.println("Strings after remove. "); /* Print each string in the list after remove.*/ for(String ball : ballList) { System.out.println(ball); } }