Fix Version/s: tbd
Compatibility Risk Description:Unlikely that anyone is depending on ConcurrentModificationExceptions being thrown
Interface Kind:Java API
ArrayList.replaceAll and Vector.replaceAll should not modify modCount, since they are not "structural modifications".
ArrayList#replaceAll increments modCount, but ArrayList subList#replaceAll does not.
Vector#replaceAll increments modCount, but Vector subList#replaceAll (inherited from AbstractList and List) does not.
ArrayList#replaceAll increments modCount, but the analogous HashMap#replaceAll does not.
List#replaceAll is specified and implemented in terms of List#set, which does not modify modCount in any implementation.
The List spec could be clearer, but the intent is that only operations that cause an object to "lose its place" in the List (modify its index) should cause ConcurrentModificationException. So element insertion and deletion cause ConcurrentModificationException, but replacing an element via List#set or ListIterator#set does not. Method replaceAll is logically just a call to List#set on every element, and the default implementation in List#replaceAll in fact does call List#set. "Structural modification" is a property that does compose over a series of operations.
There are valid uses of replaceAll that should not break concurrent readers, e.g. a periodic call to Vector.replaceAll(x -> optimized(x)). In such a scenario incrementing modCount introduces a rare race instead of detecting one.
The List interface is concurrency-hostile precisely because the mapping from index to element can change. But if a List is used in such a way that this cannot happen, array-style (like AtomicReferenceArray), then concurrent use becomes quite reasonable. Especially if there is only one writer thread.
Remove modCount++ statements in the source code.
No spec changes, although the existing spec for modCount and "structural modification" could be improved in a separate effort (JDK-8203663).