
1. Overview
In this article, we will see whether the ArrayList is a fail fast. To learn more about other Java concepts, refer to our articles.
2. Fail fast vs fail-safe Iterators
First, let’s learn the concept of fail-fast and fail-safe iterators.
If the code changes the list structurally after the creation of an Iterator
, except through the Iterator’s own remove
or add
methods, the Iterator
will throw a ConcurrentModificationException
on the best effort basis. Thus faced with concurrent modification, the Iterator
fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Iterators
that do this are known as fail-fast iterators.
Fail-Safe iterators don’t abort the operations with a failure and try to avoid raising failures as much as possible.
3. Is ArrayList a fail fast
Yes, the ArrayList is a fail-fast.
The iterators returned by this class such as iterator
and listIterator
methods are fail-fast.
Note that Java doesn’t guarantee the fail-fast behavior of an iterator as it is impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException
on a best-effort basis.
For example, the following code changes the state of the ArrayList after creating an Iterator resulting in ConcurrentModificationException
.
private static void checkFailFastBehavior() { ArrayList<String> stringList = new ArrayList<String>(); stringList.addAll(Arrays.asList("Apple", "Orange", "Mango")); Iterator iterator = stringList.iterator(); while (iterator.hasNext()) { String value = (String) iterator.next(); System.out.println(value + "\n"); stringList.add("Strawberry"); } }
Apple Exception in thread "main" java.util.ConcurrentModificationExceptionat java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043) at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997) at HelloWorld.checkFailFastBehavior(HelloWorld.java:17) at HelloWorld.main(HelloWorld.java:7)
It doesn’t mean that you would get this error only in the while loop. When you change the list after creating an iterator, this happens.
For example, the following code also results in the ConcurrentModificationException
.
private static void checkFailFastBehavior() { ArrayList<String> stringList = new ArrayList<String>(); stringList.addAll(Arrays.asList("Apple", "Orange", "Mango")); Iterator iterator = stringList.iterator(); stringList.add("Strawberry"); }
You can change the elements in the ArrayList after iterating through the list completely using Iterator.
private static void checkFailFastBehavior() { ArrayList<String> stringList = new ArrayList<String>(); stringList.addAll(Arrays.asList("Apple", "Orange", "Mango")); Iterator iterator = stringList.iterator(); while (iterator.hasNext()) { String value = (String) iterator.next(); System.out.println(value + "\n"); } stringList.add("Strawberry"); }
Note that this exception does not always show that a different thread has concurrently changed this ArrayList object. If a single thread issues a sequence of method invocations that violates the contract, the Iterator throws this exception. As discussed earlier, if a thread changes a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
Note that this fail-fast behavior cannot guarantee failure in terms of multiple threads as it is impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException
on a best-effort basis.
For example, the following code may or may not throw exception. The Runnable
task waits for 2 secs after creating the Iterator. Though the Main thread changes the ArrayList after Iterator creation, it may not throw an ConcurrentModificationException
.
import java.util.*; import java.util.concurrent.*; class HelloWorld { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); ArrayList<String> stringList = new ArrayList<String>(); stringList.addAll(Arrays.asList("Apple", "Orange", "Mango")); Runnable runnableTask = () -> { try { checkFailFastBehavior(stringList); } catch (InterruptedException e) { e.printStackTrace(); } }; executor.execute(runnableTask); stringList.add("Strawberry"); } private static void checkFailFastBehavior(ArrayList<String> stringList) throws InterruptedException { Iterator iterator = stringList.iterator(); Thread.sleep(2000); while (iterator.hasNext()) { String value = (String) iterator.next(); System.out.println(value + "\n"); } } }
java -cp /tmp/C5txWmlex5 HelloWorld Apple Orange Mango Strawberry
4. Conclusion
To sum up, we have learned whether the ArrayList iterator is fail-safe or not.