I wrote the following code to test the side effect of modifying the backing collection of a stream
List<Integer> x = new ArrayList<>(Arrays.asList(1, 11, 21));
x.stream().filter(i -> {
if (i < 10) {
x.remove(i);
return false;
}
return true;
}).forEach(System.out::println);
The output will be
21
Exception in thread "main" java.lang.NullPointerException
Can anyone tell me what's going here? In particular, where is the NullPointerException coming from? Thank you.
The stream method obtains a Stream from a java. util. Collection or a Java array. The stream operations do not modify the original collection object.
A side effect is an action taken from a stream operation which changes something externally. The change may be changing some variable values in the program or it can be sending a JMS message, sending email, printing with System.
Introduced in Java 8, the Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.
Your are violating the contract of filter
by passing an interfering predicate. See here for a good explanation of the non-interference requirement. It specifically says:
The need for non-interference applies to all pipelines, not just parallel ones. Unless the stream source is concurrent, modifying a stream's data source during execution of a stream pipeline can cause exceptions, incorrect answers, or nonconformant behavior.
So the answer to your question is that the version of java you are running failed in that particular way with that particular data due to some unspecified implementation detail. A different version of java might fail in some other way or not throw anything and produce the answer you expected or produce an incorrect answer.
You can try it with a Collection
that is specified to produce a CONCURRENT spliterator:
Collection<Integer> x = new ConcurrentLinkedQueue<>(Arrays.asList(1, 11, 21));
It should do what you expect and not throw any exceptions.
To understand it lets make a schema about what happen exactly :
Your list look like this :
+---+ +---+ +---+
| 1 | --> |11 | --> |21 |
+---+ +---+ +---+
0 1 2
Now the filter is work which will iterate over your list index by index
First Iteration (index = 0, i = 1)
check if i < 10
-> yes then remove it from the list. now look like this :
+---+ +---+ +-----+
|11 | --> |21 | --> |null |
+---+ +---+ +-----+
0 1 2
Second Iteration (index = 1, i = 21 not 11)
check if i < 10
-> yes then remove it from the list. now look like this :
+---+ +-----+ +-----+
|21 | --> |null | --> |null |
+---+ +-----+ +-----+
0 1 2
Third Iteration (index = 2, i = null)
check if i < 10
which mean null < 10
-> This will throw NullPointerException
.
The question now, why you do that? all you need is just :
x.removeIf(i -> i < 10);
x.forEach(System.out::println);
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