fail-fast vs fail-safe iterator in Java
Reference
[1] http://netjs.blogspot.co.uk/2015/05/fail-fast-vs-fail-safe-iterator-in-java.html
The collections which are there from Java 1.2 (or even legacy) like ArrayList, Vector, HashSet, HashMap all have fail-fast iterator whereas Concurrent collections added in Java 1.5 like ConcurrrentHashMap, CopyOnWriteArrayList, CopyOnWriteArraySet have fail-safe iterator
The key differences between the fail-fast and fail-safe iterator
- fail-fast iterator throws ConcurrentModificationException if the underlying collection is structurally modified in any way except through the iterator‘s own remove or add methods.
fail-safe iterator doesn‘t throw ConcurrentModificationException. - fail-fast iterator work on the original collection.
fail-safe iterator makes a copy of the underlying structure and iteration is done over that snapshot. Drawback of using a copy of the collection rather than original collection is that the iterator will not reflect additions, removals, or changes to the collection since the iterator was created. - fail-fast iterator provides remove, set, and add operations. Note that not all the iterators support all these methods. As exp.ListIterator supports add() method but the general iterator doesn‘t.
With fail-safe iterator element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
fail-fast iterator in Java
An iterator is considered fail-fast if it throws a ConcurrentModificationException under either of the following two conditions:
- In multi-threaded environment, if one thread is trying to modify a Collection while another thread is iterating over it.
- Even with single thread, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
fail-fast iterator fails if the underlying collection is structurally modified at any time after the iterator is created, thus the iterator will throw a ConcurrentModificationException if the underlying collection is structurally modified in any way except through the iterator‘s own remove or add (if applicable as in list-iterator) 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 a structural modification.
Mostly Iterators from java.util package throw ConcurrentModificationException if collection was modified by collection‘s methods (add / remove) while iterating
Also note that according to Oracle Docs fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
1) The following codes will throw ConcurrentModificationException
public class FailFastModification { public static void main(String[] args) { // creating map Map <String,String> cityMap = new HashMap<String,String>(); cityMap.put("1","New York City" ); cityMap.put("2", "New Delhi"); cityMap.put("3", "Newark"); cityMap.put("4", "Newport"); // getting iterator Iterator <String> itr = cityMap.keySet().iterator(); while (itr.hasNext()){ System.out.println(cityMap.get(itr.next())); // trying to add new value to a map while iterating it, will throw ConcurrentModificationException cityMap.put("5", "New City"); } } }
2) The following method to just update values is allowed and will not throw ConcurrentModificationException
public class FailFastModification { public static void main(String[] args) { // creating map Map <String,String> cityMap = new HashMap<String,String>(); cityMap.put("1","New York City" ); cityMap.put("2", "New Delhi"); cityMap.put("3", "Newark"); cityMap.put("4", "Newport"); // getting iterator Iterator <String> itr = cityMap.keySet().iterator(); while (itr.hasNext()){ System.out.println(cityMap.get(itr.next())); // updating existing value while iterating cityMap.put("3", "New City"); } } }
2) The following method to remove values using iterator itself is allowed and will not throw ConcurrentModificationException
public class FailFastModification { public static void main(String[] args) { Map <String,String> cityMap = new HashMap<String,String>(); cityMap.put("1","New York City" ); cityMap.put("2", "New Delhi"); cityMap.put("3", "Newark"); cityMap.put("4", "Newport"); System.out.println("size before iteration " + cityMap.size()); Iterator <String> itr = cityMap.keySet().iterator(); while (itr.hasNext()){ System.out.println(cityMap.get(itr.next())); // removing value using iterator remove method itr.remove(); } System.out.println("size after iteration " + cityMap.size()); } }
Fail Safe iterator
In case of fail-safe iterator, ConcurrentModificationException is not thrown as the fail-safe iterator makes a copy of the underlying structure and iteration is done over that snapshot.
Since iteration is done over a copy of the collection so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.
Drawback of using a copy of the collection rather than original collection is that the iterator will not reflect additions, removals, or changes to the collection since the iterator was created. Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
Iterator of CopyOnWriteArrayList is an example of fail-safe Iterator also iterator provided by ConcurrentHashMap keySet is fail-safe and never throws ConcurrentModificationException.
Look at the following example of a fail-safe iterator
public class FailSafeTest { public static void main(String[] args) { List <String> cityList = new CopyOnWriteArrayList<String>(); cityList.add("New York City"); cityList.add("New Delhi"); cityList.add("Newark"); cityList.add("Newport"); Iterator<String> itr = cityList.iterator(); while (itr.hasNext()) { System.out.println(itr.next()); cityList.add("NewCity"); //itr.remove(); } } }
This program won‘t throw ConcurrentModificationException as iterator used with CopyOnWriteArrayList is fail-safe iterator.
If we uncomment the line //itr.remove(); this program will throw UnsupportedOperationException as fail-safe iterator does not support remove, set, and add operations.
Summary
- An iterator is considered fail-fast if it throws a ConcurrentModificationException in case the underlying collection‘s structure is modified.
- While iterating a list or a map values can be updated, only if an attempt is made to add or remove from the collection ConcurrentModificationException will be thrown by fail-fast iterator.
- Fail-fast iterators throw ConcurrentModificationException on a best-effort basis and fail-fast behavior of an iterator cannot be guaranteed.
- fail-safe iterator works with a copy of the collection rather than the original collection thus interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException.
- remove, set, and add operations are not supported with fail-safe iterator.
fail-fast vs fail-safe iterator in Java