I have the following code
final String arr[] = {"A", "B", "C", "D"};
final List<String> list1 = new ArrayList<>();
final List<String> list2 = Arrays.asList(arr).parallelStream()
.peek(s -> list1.add(s + s))
.map(s -> s + s)
.collect(Collectors.toList());
System.out.println(list1);
System.out.println(list2);
And I get this following result:
[null, AA, DD, null]
[AA, BB, CC, DD]
Why some of the elements are showing null
in list1? I know I'm supposed to use SynchronizedList
with parallelStream
, but null
values are surprising to me. As soon as you add something to a list that slot should be set and next execution should have added another value to it.
ArrayList
is not a thread-safe collection, and you try to update it from numerous threads with parallelStream
, what you see is the result of resizing
the internal array of that ArrayList
- that operation is not thread safe either, you first increase the array and then move elements to it : may be elements have not been moved and you try to access it.
To make it more explicit, when resizing happens a new array is created with an increased size - this new array if full of null
s only after the copying happens.
It's really un-predictable what will happen, just don't rely on any of these.
EDIT
I can't post this as a comment, but here is some code proving that null
can be seen:
List<Integer> result = IntStream.iterate(0, i -> i + 1)
.takeWhile(i -> {
String arr[] = { "A", "B", "C", "D", "E", "F", "G", "H", "R", "T" };
List<String> list = new ArrayList<>();
Arrays.stream(arr)
.parallel()
.peek(list::add)
.forEach(x -> {
});
if (list.contains(null)) {
System.out.println("It took " + i + " steps to see a null");
return false;
}
return true;
})
.boxed()
.collect(Collectors.toList());