mapToDouble() 对于使用 Java 8 流对 List<Double> 进行求和真的必要吗?

2022-08-31 13:21:55

据我所知,使用Java 8流求和的方法是这样的:List<Double>

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

对我来说,这似乎有点粗糙 - 只是lambdas和stream应该省略的那种样板“仪式”。mapToDouble(Double::doubleValue)

最佳实践告诉我们,首选实例而不是数组,但对于这种求和,数组似乎更干净: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历史上的决策,特别是在泛型的实现方式及其与非对象基元的关系方面,某种取消装箱将永远是必要的。他指出,从理论上讲,编译器可以直观地进行拆箱并允许更简短的代码,但这尚未实现。

@Holger展示了一个非常接近我询问的表现力的解决方案:

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

我不知道新的静态方法。加上1.8,它似乎是为了我所描述的目的。我还发现和.展望未来,我肯定会使用这个成语进行此类操作和类似操作。Double.sum()Double.min()Double.max()List<Double>


答案 1

有没有办法挤进去自动装箱,使它更短?

是的,有。你可以简单地写:

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

这使得取消装箱变得隐含,但当然不会提高效率。

由于装箱的,因此取消装箱是不可避免的。另一种方法是:List

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

它不做一个,但仍然允许读取代码为“...总和”。mapToDouble


答案 2

对我来说,lambda和流似乎应该省去。mapToDouble(Double::doubleValue)

使用的需求是通过类型擦除实现泛型的决定的结果,基本上关闭了在泛型中使用基元的任何可能性的大门。正是这个决定使得有必要创建类的 、 和类族 - 以提供基于流的取消装箱。mapToDoubleDoubleStreamIntStreamLongStream

有没有办法挤进去自动装箱,使它更短?或者这只是目前的技术水平?

不幸的是,目前还没有:尽管从理论上讲,编译器可以找出可以隐式转换为隐式的,就像原语被解装一样,但这还没有完成。Stream<Double>DoubleStream

就基于阵列的解决方案而言,它是三者中效率最高的。但是,它不像其他两个那样灵活:一个 with 允许您对自定义类的任何属性求和,而最后一个允许您执行其他类型的聚合。mapToDouble

reduce(....)sum()

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


推荐