mapToDouble()对于使用Java 8stream汇总List <Double>真的是必需的吗?

据我所知,使用Java 8stream来对List<Double>进行求和的方式是这样的:

 List<Double> vals = . . . ; double sum = vals.stream().mapToDouble(Double::doubleValue).sum(); 

对于我来说, mapToDouble(Double::doubleValue)似乎是一种蹩脚的 – 就是那种lambda和stream应该放弃的样板“仪式”。

最佳实践告诉我们要优先使用List实例而不是数组,而对于这种总结,数组看起来更清晰:

 double[] vals = . . . ; double sum = Arrays.stream(vals).sum(); 

当然,可以这样做:

 List<Double> vals = . . . ; double sum = vals.stream().reduce(0.0, (i,j) -> i+j); 

但是reduce(....)sum()长得多。

我知道这与Java的非对象原语需要改进的方式有关,但是我仍然在这里错过了一些东西吗? 有办法挤压自动装箱,使这个更短吗? 或者这仅仅是当前的艺术状态?


更新 – 答案摘要

以下是对以下答案的摘要。 虽然我在这里有一个总结,但我敦促读者自己仔细阅读答案。

@dasblinkenlight解释说,由于在Java历史上进一步的决定,特别是generics被实现的方式以及它们与非对象原语的关系,总是需要某种拆箱。 他指出,编译器理论上可以直观的拆箱,并允许简单的代码,但是这还没有实现。

@Holger显示的解决scheme非常接近我所问的expression性:

 double sum = vals.stream().reduce(0.0, Double::sum); 

我不知道新的静态Double.sum()方法。 加上1.8,似乎是为了我描述的目的。 我还发现Double.min()Double.max() 。 outlook未来,我一定会在List<Double>和类似的东西上使用这个成语。

对我来说, mapToDouble(Double::doubleValue)似乎是什么lambdas和stream应该放弃。

使用mapToDouble的需要是决定通过types擦除来实现generics的结果,基本上closures了在generics中使用原语的可能性。 创buildDoubleStreamIntStreamLongStream系列类的决定是相同的 – 为了提供基于stream的拆箱。

有办法挤压自动装箱,使这个更短吗? 或者这仅仅是当前的艺术状态?

不幸的是,目前还不是:虽然从理论上说,编译器可能会发现Stream<Double>可以隐式地转换为DoubleStream ,与原语被拆箱的方式一样,这还没有完成。

就基于arrays的解决scheme而言,这是三者中最有效的。 但是,它不像其他两个那样灵活:使用mapToDouble可以将自定义类的任何属性相加,而最后一个可以执行其他types的聚合。

reduce(....)sum()长得多

我同意,这种方法在可读性方面比mapToDouble差。

有办法挤压自动装箱,使这个更短吗?

就在这里。 你可以简单地写:

 double sum = vals.stream().mapToDouble(d->d).sum(); 

这使拆箱隐含,但当然,不增加效率。

由于List 装箱,拆箱是不可避免的。 另一种方法是:

 double sum = vals.stream().reduce(0.0, Double::sum); 

它不做一个mapToDouble但仍然允许读取代码为“… sum”。

这是另一种方式来做到这一点。 如果您只需要DoubleIntegerLong列表中的总和,平均值,最小值,最大值等,可以使用其中一个可用的Collectors ,例如:

 List<Double> doubles = Arrays.asList(3.14, 5.15, 4.12, 6.); System.out.println( doubles.stream() .collect(Collectors.summingDouble(d -> d)) ); 

将打印18.41

请注意,方法名称是summmingDouble ,还有另一个方法称为summarizingDouble ,它返回DoubleSummaryStatistics ,其中包含所有基本的math运算结果。