The JavaDoc of stream package, says, "if the source of a stream is a List containing [1, 2, 3], then the result of executing map(x -> x*2) must be [2, 4, 6]. "
I have been trying to verify this behavior using the following code but the output doesn't match expectations.
Optional<String> result = Stream.of(1, 2, 3, 4).parallel().map(i -> {
try {
if (i == 1) {
Thread.sleep(1000);
}
} catch (Exception e) {
}
return "" + i;
}).peek(x -> {
System.out.println(Thread.currentThread().threadId() + " Peeking " + x);
}).findFirst();
System.out.println(result);
The output is:
1 Peeking 3
23 Peeking 4
21 Peeking 2
22 Peeking 1
Optional[1]
Shouldn't the outgoing stream from the map operation have "1" first? Also, even if I insert an unordered() operation before parallel, the result is still Optional1. Why? Shouldn't findFirst just return any value in that case instead of waiting for the operation for 1 to complete?
Only when I remove unordered() and parallel() operations, do I see the output as:
1 Peeking 1
Optional[1]
Meaning, findFirst() does short circuit. But why is it not short circuiting when the stream is unordered and parallel?
Based on @user2357112 comment I tried it by changing the peek operation to map as follows:
map(x -> {
System.out.println("Remapping " + x);
return "x"+x;
})
The output is still not in order:
Remapping 2
Remapping 4
Remapping 3
Remapping 1
Optional[x1]
The output is:
1 Peeking 3 23 Peeking 4 21 Peeking 2 22 Peeking 1 Optional[1]
Shouldn't the outgoing stream from the map operation have "1" first?
No.
You are asking for EXECUTION ORDER -- Kick off 1
first, then kick off 2
next, and so on.
No. Parallel streams by definition do NOT give you execution order. EVEN IF YOU PUT AN ordered()
ON YOUR PARALLEL STREAM, YOU WILL NOT GET EXECUTION ORDER.
Putting an ordered()
on your stream gives you RESULT ORDER. Meaning, your stream will order results ONCE THEY GET TO THE TERMINAL OPERATION. Not before then.
Also, even if I insert an
unordered()
operation before parallel, the result is stillOptional[1]
. Why? Shouldn'tfindFirst()
just return any value in that case, instead of waiting for the operation for1
to complete?
I tried this myself and I got Optional[3]
. I am using an early access version of Java 23. Can you try running the below snippet for me, and see if you still get Optional[1]
as your answer?
Optional<String> result = Stream.of(1, 2, 3, 4).unordered().parallel().map(i -> {
try {
if (i == 1) {
Thread.sleep(1000);
}
} catch (Exception e) {
}
return "" + i;
}).peek(x -> {
System.out.println(Thread.currentThread().threadId() + " Peeking " + x);
}).findFirst();
If you get Optional[1]
, please also tell me your operating system, your version of Java, and the distributor (Oracle, Amazon, etc.).
Only when I remove unordered() and parallel() operations, do I see the output
So, you are doing Stream.of()
. That is your source. And it has encounter order, meaning that is the same as saying ordered()
on your stream. Because of that, it makes sense why it grabbed Optional[1]
.
By the way, I told you that once you use parallel()
, you CANNOT get EXECUTION ORDER. If you want EXECUTION ORDER, you should use a non-parallel stream, also known as a sequential stream. Sequential means you only do 1 at a time. THAT will give you EXECUTION ORDER. That is why the result for this one is as you expected -- because Stream.of()
is a SEQUENTIAL stream. So, you get EXECUTION ORDER. But as a result, you lose parallelism.
YOU CANNOT HAVE EXECUTION ORDER AND PARALLELISM TOGETHER. If you want that, build your own solution or do something non-standard.