In Java Collections framework you would have seen that the Collections like ArrayList, HashMap return an iterator that is termed as fail-fast iterator where as the concurrent collections like CopyOnWriteArrayList, ConcurrentHashMap return an iterator that is termed as fail-safe iterator. In this post we’ll see what are fail-fast and fail-safe iterators in Java and difference between fail-fast and fail-safe iterators in Java.
fail-fast iterator in Java
A fail-fast iterator in Java is an iterator which throws a ConcurrentModificationException if the underlying collection is structurally modified at any time after the iterator is created except through the iterator's own remove or add (applicable in case of ListIterator) methods.
Note that structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element in case of list or changing the value associated with an existing key in case of map is not termed as structural modification.
fail-fast iterator internal working in Java
In the collections like ArrayList, HashSet where iterator is fail-fast the implementation classes have an int variable modCount that stores the count of the number of times the collection has been structurally modified.
Whenever a next value is fetched while iteration of the collection a check is made for the equality of modCount and expectedModCount if both are not equal the iterator fails throwing a ConcurrentModificationException.
fail-fast iterator Java example
Let’s see an example where a value is added to the HashMap while it is iterated.
public class FailFastDemo { public static void main(String[] args) { Map<String, String> carMap = new HashMap<String, String>(); carMap.put("1", "Audi"); carMap.put("2", "BMW"); carMap.put("3", "Jaguar"); carMap.put("4", "Mini Cooper"); // iterating map Iterator<Map.Entry<String, String>> itr = carMap.entrySet().iterator(); while(itr.hasNext()) { Map.Entry<String, String> entry = itr.next(); System.out.println("Key is " + entry.getKey() + " Value is " + entry.getValue()); // adding value to Map carMap.put("5", "Mercedes"); } } }Output
Exception in thread "main" Key is 1 Value is Audi java.util.ConcurrentModificationException at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1498) at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1531) at java.base/java.util.HashMap$EntryIterator.next(HashMap.java:1529) at com.knpcode.FailFastDemo.main(FailFastDemo.java:19)
As you can see ConcurrentModificationException is thrown because of the attempt to structurally modify the Map while it is iterated using an iterator.
As already stated merely modifying the value in a collection that returns fail-fast iterator won’t result in ConcurrentModificationException being thrown.
public class FailFastDemo { public static void main(String[] args) { Map<String, String> carMap = new HashMap<String, String>(); carMap.put("1", "Audi"); carMap.put("2", "BMW"); carMap.put("3", "Jaguar"); carMap.put("4", "Mini Cooper"); // iterating map Iterator<Map.Entry<String, String>> itr = carMap.entrySet().iterator(); while(itr.hasNext()) { Map.Entry<String, String> entry = itr.next(); if(entry.getKey().equals("3")) { // Modifying value for existing key carMap.put("3", "Mercedes"); } System.out.println("Key is " + entry.getKey() + " Value is " + entry.getValue()); } } }Output
Key is 1 Value is Audi Key is 2 Value is BMW Key is 3 Value is Mercedes Key is 4 Value is Mini Cooper
As you can see here value is modified for an existing key which is OK and doesn't throw ConcurrentModificationException.
Using iterator’s remove method
You can use iterator’s remove method to remove an element while iterating without causing ConcurrentModificationException.
public class FailFastDemo { public static void main(String[] args) { Map<String, String> carMap = new HashMap<String, String>(); carMap.put("1", "Audi"); carMap.put("2", "BMW"); carMap.put("3", "Jaguar"); carMap.put("4", "Mini Cooper"); // iterating map Iterator<Map.Entry<String, String>> itr = carMap.entrySet().iterator(); while(itr.hasNext()) { Map.Entry<String, String> entry = itr.next(); if(entry.getKey().equals("3")) { // removing using iterator's remove method itr.remove(); } } System.out.println("** After element removal **"); for(String key : carMap.keySet()){ System.out.println("Key is " + key + " Value is " + carMap.get(key)); } } }Output
** After element removal ** Key is 1 Value is Audi Key is 2 Value is BMW Key is 4 Value is Mini Cooper
fail-safe iterator in Java
A fail-safe iterator in Java is an iterator that works on the snap shot of the collection at some point of time. So any structural modification in the underlying collection is not guaranteed to be reflected while iterating.
The implementation differs in different concurrent collections. For example in case of CopyOnWriteArrayList, iterator method uses a reference to the state of the array at the point that the iterator was created. The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
In case of ConcurrentHashMap iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. So in case of ConcurrentHashMap it may show the modified value.
fail-safe iterator ConcurrentHashMap example
public class FailSafeDemo { public static void main(String[] args) { Map<String, String> carMap = new ConcurrentHashMap<String, String>(); carMap.put("1", "Audi"); carMap.put("2", "BMW"); carMap.put("3", "Jaguar"); carMap.put("4", "Mini Cooper"); // iterating map Iterator<Map.Entry<String, String>> itr = carMap.entrySet().iterator(); while(itr.hasNext()) { Map.Entry<String, String> entry = itr.next(); System.out.println("Key is " + entry.getKey() + " Value is " + entry.getValue()); carMap.putIfAbsent("5", "Mercedes"); } System.out.println("Size- " + carMap.size()); } }Output
Key is 1 Value is Audi Key is 2 Value is BMW Key is 3 Value is Jaguar Key is 4 Value is Mini Cooper Key is 5 Value is Mercedes Size- 5
The element which was added while iterating the Map is also displayed.
fail-safe iterator CopyOnWriteArrayList example
public class FailSafeDemo { public static void main(String[] args) { List<String> carList = new CopyOnWriteArrayList<>(); carList.add("Audi"); carList.add("BMW"); carList.add("Jaguar"); carList.add("Mini Cooper"); boolean addFlag = false; Iterator<String> itr = carList.iterator(); while(itr.hasNext()) { System.out.println(itr.next()); // add element to the list if(!addFlag){ carList.add("Mercedes"); addFlag = true; } } System.out.println("-- List after addition -- "); itr = carList.iterator(); while(itr.hasNext()) { System.out.println(itr.next()); } } }Output
Audi BMW Jaguar Mini Cooper -- List after addition -- Audi BMW Jaguar Mini Cooper Mercedes
Here you can see that the element is added to the CopyOnWriteArrayList while iteration but it is not reflected in that iteration as in case of CopyOnWriteArrayList iterator uses a reference to the underlying array at the point that the iterator was created.
Fail-fast Vs Fail-safe iterator in Java
- Fail-fast iterator throws ConcurrentModificationException if the underlying collection is modified at any time after the iterator is created.
Fail-safe iterator doesn’t throw ConcurrentModificationException if the underlying collection is modified at any time after the iterator is created. - In case of fail-fast iterator, since the underlying collection can’t be changed so no need to clone the collection or take a snap shot of collection at any point of time.
In case of fail-safe iterator, either copy of underlying collection is used or a snap shot of collection at any point of time is used making the fail-safe iterator weakly consistent. - Fail-fast iterators have their own remove and add methods that can be used to modify the collection while iterating.
In case of fail-safe iterator based on the implementation element-changing operations on iterators themselves (remove, set, and add) may not be supported as in the case of CopyOnWriteArrayList. These methods throw UnsupportedOperationException.
That's all for the topic Fail-fast And Fail-safe Iterators in Java. If something is missing or you have something to share about the topic please write a comment.
You may also like
No comments:
Post a Comment