RFR: JDK-8277095 : Empty streams create too many objects

kabutz duke at openjdk.java.net
Mon Nov 15 19:56:39 UTC 2021


On Mon, 15 Nov 2021 18:08:22 GMT, Philippe Marschall <duke at openjdk.java.net> wrote:

> I have a similar project at [empty-streams](https://github.com/marschall/empty-streams). A couple of notes:
> 
> 1. I found the need for streams to be stateful. I had need for the following state:
>    
>    1. closed
>    2. ordered
>    3. parallel
>    4. sorted

I found the need for ALL the Spliterator characteristics, plus some others that I interleaved between the Spliterator bits. That way I could fit them into a single int.

Parallel was too tricky though, so in that case I return a new stream built with the standard StreamSupport(spliterator, true).

>    5. closeHandler

Similarly when someone calls closeHandler(), I also return a new stream built with StreamSupport.

>    6. comparator (on EmptyStream)

Yes, we need this for TreeSet and ConcurrentSkipListSet. I was hoping to avoid it, but there is no way around if we want to be completely correct.

>       A shared instance can not be used because of `#close`.

Agreed. I fixed that.

> 2. I have a `PrimitiveIterator` that short circuits `#next` and `#forEachRemaining` as well.

Oh that's a good suggestion - thanks.
> 3. I made many methods just return `this` after checking for operated on and closed:
>    
>    1. `#filter` `#map`, `#flatMap`, `#mapMulti`, `#distinct`, `#peek`, `#limit`, `#skip`, `#dropWhile`, `#takeWhile`.

I now always return a new EmptyStream. Fortunately escape analysis gets rid of a lot of objects.

>    2. These do a state change state as well `#sequential`, `#parallel`, `#unordered`, `#sorted`, `#onClose`.

unordered() only does a state change if it currently ORDERED, otherwise it is correct to return this. The logic is convoluted and weird though. I have a more thorough test case that I need to incorporate into my EmptyStreamTest and which tests all the JDK collections, including the wrapper and immutable collections.

> 4. I made my `EmptyBaseStream` implement `BaseStream` and make `EmptyIntLongDoubleStream` extend from this class as `IntLongDoubleStream` all extend `BaseStream`. This allowed me to move the following methods up in the hierarchy `#isParallel` , `#onClose`, `#sequential`, `#parallel`, `#unordered`.

Interesting idea. I'm not sure if that would work for me. I will have a look if I can also do this, but I doubt it. A lot of the methods, especially the primitive ones, are repeats of each other. It might be really nice to put that into a common superclass. 

> 5. Is there any reason why you make `#parallel` "bail out" to `StreamSupport` rather than just do a state change?

I tried to keep the characteristics identical to what we had before and parallel seemed particularly challenging to get right. I might attempt this again at a later stage, but for now I want to keep it like it is. I don't think parallel() is the most common use case, except with very large collections perhaps. Even though I thought I was careful with the characteristics, I still have a few edge cases I need to iron out.

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

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


More information about the core-libs-dev mailing list