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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With