Fail-fast vs Fail-safe in Java Collections

Fail-fast vs Fail-safe in Java Collections

Considering the following case:

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(0);
list.add(1);
list.add(2);

Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) list.remove(iterator.next());
}
}

If we run the above code it would throw an ConcurrentModificationException. This is because ArrayList is a collection in the java.util, and all collections in java.util are fail-fast. That is, if the collection is modified while using iterator to iterate over it, the iterators would throw an ConcurrentModificationException.

However, if we use a fail-safe collection(i.e. the collections in java.util.concurrent), it would not throw an exception, e.g.

1
2
3
4
5
6
7
8
9
10
11
public class Main {
public static void main(String[] args) {
List<Integer> list = new CopyOnWriteArrayList<>();
list.add(0);
list.add(1);
list.add(2);

Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) list.remove(iterator.next());
}
}

Fail-fast Iterator Internal Working

Take ArrayList as an example, let’s walk through why there is an ConcurrentModificationException:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyArrayList<E> {
// 1. every fail fast collection has a field modCount to store
// the times that the collection has been modified
int modCount = 0;

// 2. when we call the list.iterator() method,
// it will create a new Itr object
public Iterator<E> iterator() {
return new Itr();
}

private class Itr implements Iterator<E> {
// 3. initially, the field expectedModCount is equal to modCount
int expectedModCount = modCount;

// 4. there is a method checkForComidification method in Itr,
// and if the modCount is modified during the iteration, an
// ConcurrentModificationException will occur
final void checkForComodification() {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}

// 5. the remove operation will definitely modify the modCount,
// therefore there is an ConcurrentModificationException
public boolean remove(Object o) {
// Code...
}
}

Fail-safe Iterator Internal Working

Take CopyOnWriteArrayList as an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyCopyOnWriteArrayList<E> {
private volatile Object[] array;

final Object[] getArray() {
return array;
}


// 1. when we call list.iterator(), a COWIterator object is created
public Iterator<E> iterator() {
return new COWIterator(getArray(), 0);
}


static final class COWIterator implements ListIterator<E> {
private final Object[] snapshot;// snapshot of MyCopyWriteArrayList.array
private int cursor;

// 2. the original collection elements are saved in snapshot,
// and the iterator mothods will work on this snapshot
private COWIterator(Object[] elements, int initialCursor) {
snapshot = elements;
cursor = initialCursor;
}
}


// 3. therefore, even if there is any change in the original collection,
// no exception will be thrown. However, the the iterator will not reflect
// the latest state of the collection
}