On Sat, 13 Nov 2021 16:59:10 GMT, liach <d...@openjdk.java.net> wrote:

>> This is a draft proposal for how we could improve stream performance for the 
>> case where the streams are empty. Empty collections are common-place. If we 
>> iterate over them with an Iterator, we would have to create one small 
>> Iterator object (which could often be eliminated) and if it is empty we are 
>> done. However, with Streams we first have to build up the entire pipeline, 
>> until we realize that there is no work to do. With this example, we change 
>> Collection#stream() to first check if the collection is empty, and if it is, 
>> we simply return an EmptyStream. We also have EmptyIntStream, 
>> EmptyLongStream and EmptyDoubleStream. We have taken great care for these to 
>> have the same characteristics and behaviour as the streams returned by 
>> Stream.empty(), IntStream.empty(), etc. 
>> 
>> Some of the JDK tests fail with this, due to ClassCastExceptions (our 
>> EmptyStream is not an AbstractPipeline) and AssertionError, since we can 
>> call some methods repeatedly on the stream without it failing. On the plus 
>> side, creating a complex stream on an empty stream gives us upwards of 50x 
>> increase in performance due to a much smaller object allocation rate. This 
>> PR includes the code for the change, unit tests and also a JMH benchmark to 
>> demonstrate the improvement.
>
> src/java.base/share/classes/java/util/Arrays.java line 5448:
> 
>> 5446:     public static <T> Stream<T> stream(T[] array, int startInclusive, 
>> int endExclusive) {
>> 5447:         var spliterator = spliterator(array, startInclusive, 
>> endExclusive);
>> 5448:         if (startInclusive == endExclusive) return Stream.empty();
> 
> Can't we just add the `if` statement to before the `spliterator` is computed?

Thanks @liach for the suggestion. The spliterator serves the purpose of 
checking the arguments. For example, if array is null, the method should throw 
a NPE. Similarly, startInclusive and endExclusive have to be in the range of 
the array length. If we swap around the lines, someone could call stream(null, 
-100, -100) and it would happily return an empty stream.

Furthermore, the empty streams can inherit characteristics of a spliterator. In 
the code you pointed out, I don't do that, but will change that. It makes it 
much easier to keep the empty streams consistent with their previous behavior. 
One might argue that it is pointless to call distinct() sorted() and 
unordered() on an empty stream and expect it to change, but it was easy enough 
to implement so that we can keep consistency FWIW.

-------------

PR: https://git.openjdk.java.net/jdk/pull/6275

Reply via email to