为什么Collectors.toList()不能处理原始集合?
(这可能与https://stackoverflow.com/a/30312177/160137有关,但我恐怕还是没有得到它。所以我这样问我的问题,希望它能够得出一个我可以更容易理解的答案。)
通常,当我有一个Stream时,我可以使用Collectors类中的一个静态方法将其转换为集合:
List strings = Stream.of("this", "is", "a", "list", "of", "strings") .collect(Collectors.toList());
然而,类似的过程不适用于原始流,正如其他人注意到的那样:
IntStream.of(3, 1, 4, 1, 5, 9) .collect(Collectors.toList()); // doesn't compile
我可以这样做:
IntStream.of(3, 1, 4, 1, 5, 9) .boxed() .collect(Collectors.toList());
或者我可以这样做:
IntStream.of(3, 1, 4, 1, 5, 9) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
问题是,为什么Collectors.toList()只对原始流执行此操作? 难道没有办法指定包装类型吗? 如果是这样,为什么这不起作用:
IntStream.of(3, 1, 4, 1, 5, 9) .collect(Collectors.toCollection(ArrayList::new)); // nope
任何见解将不胜感激。
好吧,让IntStream
提供带签名的方法是没有问题的
执行隐式装箱。 它可以像return boxed().collect(collector);
。
问题是“为什么要这样”或反过来:为什么原始流专业化存在呢?
它们仅仅出于性能原因而存在,以提供流操作而不需要拳击开销。 因此,显而易见的设计决定不包括通用Stream
接口中已存在的任何操作,因为所有这些操作都可以简单地调用boxed().genericOperation(…)
。
答案是,您已将相关但不同的想法联系起来 。 它是关于提供一个不接受通用Collector
的collect
方法,而是一个像Collector.ofInt
这样的原始特化,它可以在没有装箱的情况下收集int
值,但是对于产生List
的收集器,不可避免地包含盒装值,它不会有任何好处。 在预建的collections家中,只有少数人真的可以避免拳击。 所有这些都是作为原始流的显式终端操作提供的( count()
, sum()
, min()
, max()
, summaryStatistics()
,…)
这是类/接口数量和潜在性能增益之间的权衡。 对于一般的流,决定是创建IntStream
, LongStream
和DoubleStream
,但是对于收集器,决定不添加这样的特化。
对于原始类型, List<>
是一个额外的次优用法。 因此, toArray
被认为足够和充足(=最佳使用)。
int[] array = IntStream.of(3, 1, 4, 1, 5, 9).toArray();
IntStream.of(3, 1, 4, 1, 5, 9) .collect(Collectors.toList()); // doesn't compile
流是int
,通用方法toList()
假定一个对象。 没有比赛。
IntStream.of(3, 1, 4, 1, 5, 9) .boxed() .collect(Collectors.toList());
流是Integer
,通用方法toList()
假定一个对象。 比赛!
IntStream.of(3, 1, 4, 1, 5, 9) .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
流是int
,就像将generics方法toList()
修复为Integer
。 除了IntStream没有接受收集器的collect()
方法,只有3参数方法。