Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)
like image 500
Joydeep Avatar asked Dec 25 '18 15:12

Joydeep


People also ask

What is the difference between concurrent and concurrent streams?

The notable exception to this are streams whose sources are concurrent collections, which are specifically designed to handle concurrent modification. Concurrent stream sources are those whose Spliterator reports the CONCURRENT characteristic.

Can I perform a concurrent reduction of a stream?

Consequently, a concurrent reduction is only possible if ordering is not important for the stream being processed. The Stream.collect (Collector) implementation will only perform a concurrent reduction if

What are streams in Java?

The classes Stream, IntStream , LongStream, and DoubleStream are streams over objects and the primitive int, long and double types. Streams differ from collections in several ways:

How are stream-bearing methods implemented?

So far, all the stream examples have used methods like Collection.stream () or Arrays.stream (Object []) to obtain a stream. How are those stream-bearing methods implemented? The class StreamSupport has a number of low-level methods for creating a stream, all using some form of a Spliterator.


2 Answers

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
like image 149
Naman Avatar answered Sep 29 '22 02:09

Naman


This is because of subList,let me explain with different scenarios

From java docs docs

For well-behaved stream sources, the source can be modified before the terminal operation commences and those modifications will be reflected in the covered elements.

Except for the escape-hatch operations iterator() and spliterator(), execution begins when the terminal operation is invoked, and ends when the terminal operation completes.

Case 1: Successful (because the source can be modified before the terminal operation commences)

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

Output :

A
B
c
d

Case 2: Failed for sublist different reference

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

Output :

A
BException 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)
at com.demo.Example.Main2.main(Main2.java:30)

case 3: Failed for sublist same reference, Both lists are different

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

Output :

94401
3042
A
B
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)
at com.demo.Example.Main2.main(Main2.java:32)

Final Conclusion

The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)

From docs subList docs

like image 23
Deadpool Avatar answered Sep 29 '22 03:09

Deadpool