I look to java 11 implementation of .foreach
method in CopyOnWriteArrayList
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
I see that it just loops the array without any locks.
Can add()
or remove()
performed concurrently with foreach
give a ConcurrentModificationException
?
In contrast to iterator()
, foreach
seems to avoid using the copy of original array on write and it uses no locks.
No. You can see that from the code that it doesn't throw
ConcurrentModificationException
:Note that
getArray()
call is not copying the array. It is declared like this:(Since
array
isvolatile
, no lock is needed to ensure thatgetArray()
returns the current version of the array.)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.Actually,
iterator()
behaves the same way asforeach
. It callsgetArray()
to get the current backing array.And if you look at the
COWIterator
class, it doesn't throwConcurrentModificationException
either.Note that this is all specified in the javadocs.
The javadocs for
CopyOnWriteArrayList
state:The javadocs for
foreach
(inIterable
) state:which is using the iterator provided by
CopyOnWriteArrayList
that doesn't throwConcurrentModificationException
; see 1.However, there is a small gotcha. A sublist of a
CopyOnWriteArrayList
is not aCopyOnWriteArrayList
, and it can produce aConcurrentModificationException
; see CopyOnWriteArrayList throwing CurrentModificationException