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

牧云@^-^@ 提交于 2019-12-07 00:01:05

问题


(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.


回答1:


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.




回答2:


For primitive types List<> is an added suboptimal usage. Hence toArray was deemed sufficient and adequate (=optimal usage).

int[] array = IntStream.of(3, 1, 4, 1, 5, 9).toArray();



回答3:


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

The stream is int, the generic method toList() assumes an object. No match.

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

The stream is Integer, the generic method toList() assumes an object. Match!

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

The stream is int, and it's like fixing the generic method toList() to Integer. Except that IntStream doesn't have a collect() method that accepts a collector, only a 3-parameter method.



来源:https://stackoverflow.com/questions/38964268/why-doesnt-collectors-tolist-work-on-primitive-collections

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!