Can using foreach
of CopyOnWriteArrayList
cause ConcurrentModificationException
in java?
No. You can see that from the code that it doesn't throw ConcurrentModificationException
:
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
Note that getArray()
call is not copying the array. It is declared like this:
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
(Since array
is volatile
, no lock is needed to ensure that getArray()
returns the current version of the array.)
Can add()
or remove()
performed concurrently with foreach
give a ConcurrentModificationException
?
A call to those methods will cause a new backing array to be created with the update. This is done holding a lock on the CopyOnWriteArrayList
, and then the array is replaced.
Meanwhile, the foreach()
call will loop over the old array as if nothing happened.
In contrast to iterator()
, foreach seems to avoid using the copy of original array on write and it uses no locks.
Actually, iterator()
behaves the same way as foreach
. It calls getArray()
to get the current backing array.
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
And if you look at the COWIterator
class, it doesn't throw ConcurrentModificationException
either.
Note that this is all specified in the javadocs.
The javadocs for CopyOnWriteArrayList
state:
"... the iterator is guaranteed not to throw ConcurrentModificationException
."
The javadocs for foreach
(in Iterable
) state:
"The default implementation behaves as if:"
for (T t : this)
action.accept(t);
which is using the iterator provided by CopyOnWriteArrayList
that doesn't throw ConcurrentModificationException
; see 1.
However, there is a small gotcha. A sublist of a CopyOnWriteArrayList
is not a CopyOnWriteArrayList
, and it can produce a ConcurrentModificationException
; see CopyOnWriteArrayList throwing CurrentModificationException