Search code examples
javajava-8java-streamconcurrentmodification

java.util.ConcurrentModificationException Streams


I was trying the following code Java 8 SE I ran it directly from eclipse, it has the below-mentioned exception also I ran it with command prompt it produces the same result.

List<String> test = new ArrayList<>();
test.add("A");
test.add("B");
test.add("c");
test = test.subList(0, 2);
Stream<String> s = test.stream();
test.add("d");
s.forEach(System.out::println);

I am not sure as to why exactly it gives the following exception

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1388)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)

Java version I am running with

java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

Solution

  • Minimal Code

    List<String> test = new ArrayList<>(Arrays.asList("java-8", "subList", "bug")).subList(0, 2);
    Stream<String> stream = test.stream();
    test.add("java-9");
    stream.forEach(System.out::println); // any terminal operation
    

    Java-8 [Bug]

    The code above executed with Java-8 throws a CME. According to the javadoc of ArrayList

    The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.

    Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

    Output:

    java-8
    subList
    Exception in thread "main" java.util.ConcurrentModificationException
    

    Question

    Under the similar guidelines, modifying a collection while it's being iterated is considered to be a programming error and consequently throwing of ConcurrentModificationException is performed on a "best-effort" basis.

    But then the question was, in the code above did we actually end up modifying the collection while it was being iterated or rather before that?

    shouldn't stream be lazy?

    On searching further for such expected behavior, found something similar reported and fixed as a bug - ArrayList.subList().spliterator() is not late-binding and this has been fixed with Java-9.

    Another bug related to this - ArrayList.subList().iterator().forEachRemaining() off-by-one-error

    Java-11 [Fixed]

    Though fixed in Java-9 according to the bug report, the actual test that I was performing was on the LTS version and the code as shared above works without any exception.

    Output:

    java-8
    subList
    java-9