long start = System.currentTimeMillis();
long count = IntStream.rangeClosed(Float.floatToIntBits(0.0f), Float.floatToIntBits(1.0f)).count();
System.out.println(count);
System.out.println((System.currentTimeMillis() - start) + "ms");
1065353217
0msReally cool trick!
I can't tell that'd be possible - I see the arguments could be evaluated at compile time, but knowing IntStream.rangeClosed will be evaluated at compile time is a leap, to me.
Did you know before you wrote the code that'd happen? Is it like C? You cross your fingers and hope the compiler unrolls?
So there's actually no reason to even invoke the streams API there.
While technically correct, in practice it is not the case.
First of all (and this relates to flerchin's comment), it's not actually done at "compile time" in the traditional sense (i.e. when you invoke `javac`). Rather, it is done at runtime when the method is compiled by the JIT.
As for this comment, the call to `IntStream.rangeClosed` will not simply be reduced to `(b - a) + 1` as you suggest (and one reason for this is that streams are far more complex internally than regular iterators). In reality, it will just be (potentially) inlined and then further optimized alongside the rest of the method's code.
Edit: the mentioned previous comment was from flerchin, not meese
Edit 2: I might have misunderstood what you were getting at there. You are sort of correct in that the `count` operation on this stream is optimized, but it is still technically going through the streams API.
How is this done?
The implementation of a `Spliterator` that is used internally by `IntStream.rangeClosed` overrides the `estimateSize` method to return `((long) upTo) - from + last`, and the `characteristics` method to return, among others, the `Spliterator.SIZED` characteristic. That characteristic indicates that `estimateSize` can be trusted to be exact, and when present, `estimateSize` will be used as a shortcut in the implementation of `Stream.count`, which can be seen in `ReduceOps.makeRefCounting`.
int count = 1;
int limit = Float.floatToIntBits(1.0f);
for (int intBits = Float.floatToIntBits(0.0f); intBits <= limit; intBits++) {
count++;
}
it will start out with the same performance as your previous one, and then after a few tries the JVM will optimize it into a constant. 1065353218, 0.91
1065353218, 0.69
1065353218, 0.00
1065353218, 0.64
1065353218, 0.64
1065353218, 0.63
1065353218, 0.66
1065353218, 0.63
1065353218, 0.63
1065353218, 0.00
1065353218, 0.00
1065353218, 0.00
...
1065353218, 0.00
Process finished with exit code 0 ((long) upTo) - from + last;
I would think it just takes less than 1 ms on first execution.https://github.com/openjdk/jdk/blob/caa841d9a52352a975394e55...
Also, in the case of your test, it's not even letting the JIT kick in, but rather using a shortcut to calculate the length of the range.