Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bizarre behavior of list

Tags:

java

I have defined list from 0-9(Integer) as below:

List<Integer> list = 
                IntStream.range(0, 10)
                         .boxed()
                         .collect(Collectors.toCollection(ArrayList::new));

When I try to remove elements using below code:

list.stream()
            .peek(list::remove)
            .forEach(System.out::println);

It should throw ConcurrentModificationException but interestingly it works for some of the elements and gives below output(threw exception at the end and removed some of the elements):

0
2
4
6
8
null
null
null
null
null
Exception in thread "main" java.util.ConcurrentModificationException

But if I have added sorted() like below:

list.stream()
            .sorted()
            .peek(list::remove)
            .forEach(System.out::println);

This worked perfectly fine and removed all the elements, I am confused why stream behaves this way.

like image 693
Nitesh Virani Avatar asked Aug 12 '15 11:08

Nitesh Virani


1 Answers

Have you looked at the source code how these classes are implemented?

The method sorted() will make a copy of your list for sorting.

It thus behaves differently: you are iterating over the sorted copy then, but removing from the first list only.

peek() is discouraged

The method peek is discouraged to use; from the JavaDoc:

This method exists mainly to support debugging

It's okay to put a System::println there or similar for development purposes, but it is not a functionality you should use for your final program.

Never attempt to modify the current stream

Read the API specification of the stream package:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference

For most data sources, preventing interference means ensuring that the data source is not modified at all during the execution of the stream pipeline.

So, in other words: you are not allowed to remove from a list that you are currently streaming, there are no guarantees that it will work.

What is happening

Your initial list is

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]        pos = 0

You print 0 and remove it, then advance to the next position. The new array is then

[1, 2, 3, 4, 5, 6, 7, 8, 9, null]     pos = 1

At the next position the value is now 2, so print and remove 2:

[1, 3, 4, 5, 6, 7, 8, 9, null, null]  pos = 2

and so on. The problem is, that because of your concurrent removals, the remainder of the list keeps on moving forward.

The ConcurrentModificationException is thrown at the end of the stream. This appears to be a performance decision (only check once for concurrent modifications, not at every iteration) for forEachRemaining only, so this stream is not fail-fast

like image 125
Has QUIT--Anony-Mousse Avatar answered Oct 21 '22 12:10

Has QUIT--Anony-Mousse