Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Collectors.toList() work on primitive collections?

(This is probably related to https://stackoverflow.com/a/30312177/160137, but I'm afraid I still don't get it. So I'm asking my question this way, in the hope that it'll lead to an answer I can more easily understand.)

Normally when I have a Stream I can convert it to a collection using one of the static methods in the Collectors class:

List<String> strings = Stream.of("this", "is", "a", "list", "of", "strings")
    .collect(Collectors.toList());

The similar process doesn't work on primitive streams, however, as others have noticed:

IntStream.of(3, 1, 4, 1, 5, 9)
    .collect(Collectors.toList());  // doesn't compile

I can do this instead:

IntStream.of(3, 1, 4, 1, 5, 9)
    .boxed()
    .collect(Collectors.toList());

or I can do this:

IntStream.of(3, 1, 4, 1, 5, 9)
    .collect(ArrayList<Integer>::new, ArrayList::add, ArrayList::addAll);

The question is, why doesn't Collectors.toList() just do that for primitive streams? Is it that there's no way to specify the wrapped type? If so, why doesn't this work either:

IntStream.of(3, 1, 4, 1, 5, 9)
    .collect(Collectors.toCollection(ArrayList<Integer>::new)); // nope

Any insight would be appreciated.

like image 510
kousen Avatar asked Aug 15 '16 23:08

kousen


1 Answers

Well, it would be no problem to let IntStream provide a method with the signature
<R,A> R collect(Collector<? super Integer,A,R> collector) performing an implicit boxing. It could be implemented as simple as return boxed().collect(collector);.

The question is “why should it” or the other way round: why do the primitive stream specializations exist at all?

They merely exist for performance reasons, to offer stream operations without boxing overhead. So it’s an obvious design decision not to include any operation which already exists in the generic Stream interface as for all these you can simply invoke boxed().genericOperation(…).

The answer, you have linked addresses a related, but different idea. It’s about providing a collect method not accepting the generic Collector, but a primitive specialization like Collector.ofInt which could collect int values without boxing, but for a collector which produces a List<Integer>, unavoidably containing boxed values, it wouldn’t have any benefit. Amongst the prebuilt collectors, only a few really could avoid boxing. All of them are provided as explicit terminal operations on the primitive streams (count(), sum(), min(), max(), summaryStatistics(), …)

It’s a trade-off between number of classes/interfaces and potential performance gain. In the case of streams in general, the decision was to create IntStream, LongStream and DoubleStream, but in case of the collectors, the decision was not to add such specializations.

like image 110
Holger Avatar answered Nov 14 '22 04:11

Holger