What is the difference between Stream.of and IntStream.range?
Please, consider this code:
System.out.println("#1");
Stream.of(0, 1, 2, 3)
.peek(e -> System.out.println(e))
.sorted()
.findFirst();
System.out.println("\n#2");
IntStream.range(0, 4)
.peek(e -> System.out.println(e))
.sorted()
.findFirst();
The output will be:
#1
0
1
2
3
#2
0
Could anyone explain, why output of two streams are different?
Solution 1:
Well, IntStream.range()
returns a sequential ordered IntStream from startInclusive(inclusive) to endExclusive (exclusive) by an incremental step of 1
, which means it's already sorted. Since it's already sorted, it makes sense that the following .sorted()
intermediate operation does nothing. As a result, peek()
is executed on just the first element (since the terminal operation only requires the first element).
On the other hand, the elements passed to Stream.of()
are not necessarily sorted (and the of()
method doesn't check if they are sorted). Therefore, .sorted()
must traverse all the elements in order to produce a sorted stream, which allows the findFirst()
terminal operation to return the first element of the sorted stream. As a result, peek
is executed on all the elements, even though the terminal operation only needs the first element.
Solution 2:
IntStream.range
is already sorted:
// reports true
System.out.println(
IntStream.range(0, 4)
.spliterator()
.hasCharacteristics(Spliterator.SORTED)
);
So when sorted()
method on the Stream is hit, internally, it will become a NO-OP.
Otherwise, as you already see in your first example, all the elements have to be sorted, only then findFirst
can tell who is "really the first one".
Just notice that this optimization only works for naturally sorted streams. For example:
// prints too much you say?
Stream.of(new User(30), new User(25), new User(34))
.peek(x -> System.out.println("1 : before I call first sorted"))
.sorted(Comparator.comparing(User::age))
.peek(x -> System.out.println("2 : before I call second sorted"))
.sorted(Comparator.comparing(User::age))
.findFirst();
where (for brevity):
record User(int age) { }