Reputation: 289
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.
Upvotes: 5
Views: 254
Reputation: 28143
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.
Upvotes: 8
Reputation: 59996
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);
Upvotes: 6