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中使用原语的可能性。 创buildDoubleStream
, IntStream
和LongStream
系列类的决定是相同的 – 为了提供基于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”。
这是另一种方式来做到这一点。 如果您只需要Double
, Integer
或Long
列表中的总和,平均值,最小值,最大值等,可以使用其中一个可用的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运算结果。