使用Java 8流将List <Double>相加是否真的需要mapToDouble()?


70

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

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

在我mapToDouble(Double::doubleValue)看来,这似乎很不礼貌-只是应该放弃lambda和数据流的样板“仪式”。

最佳实践告诉我们,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);

我没有意识到新的静态Double.sum()方法。加上1.8,似乎是出于我所描述的目的。我还发现了Double.min()Double.max()。展望未来,我一定会将此习语用于类似的操作List<Double>


2
这个问题似乎是题外话,因为它不是问题,而是伪装成问题的博客文章!

11
我敦促您重新考虑。这是一个诚实的问题:“是否有某种方法可以压缩自动装箱以使其更短?” 我表达的任何观点都有助于解释该问题的动机。答案启发了我我不知道的Java类型和原始包装上的新方法。他们帮助我编写了更好的代码。这简直就是“咆哮”。
sparc_spread 2014年

Answers:


34

在我mapToDouble(Double::doubleValue)看来,似乎不需要什么lambda和小溪。

使用的需要mapToDouble是决定通过类型擦除实现泛型的结果,实质上是在使用泛型内部使用基元的任何可能性上都关了门。这是使得有必要创建相同的决定DoubleStreamIntStreamLongStream家庭类-提供基于流的拆箱。

有什么方法可以压缩自动装箱以使其更短吗?还是这只是当前的最新状态?

不幸的是,目前还不是:尽管从理论上说编译器有可能找出Stream<Double>可以DoubleStream隐式转换为原始类型的方法,但仍未完成。

就基于阵列的解决方案而言,它是这三种方法中效率最高的。但是,它不如其他两个灵活,一个mapToDouble可以让您对自定义类的任何属性求和,而最后一个可以让您执行其他类型的聚合。

reduce(....) 比这更长 sum()

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


1
恩,老实说,我希望我错过了API中的某些功能,但我想不是。我非常感谢您将解释放在Java输入的整个历史环境中,以及围绕它的思考过程。这使得这种情况更易于理解,这是语言设计所固有的,而不仅仅是缺少API调用。我认为这是一个有用的参考。
sparc_spread 2014年

3
我真的很期待Valhalla项目,mail.openjdk.java.net
pipermail / discuss /

49

有什么方法可以压缩自动装箱以使其更短吗?

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

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

这使得取消装箱是隐式的,但当然不会增加效率。

由于List 装箱,因此无法取消装箱。一种替代方法是:

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

它不执行“ a”操作,mapToDouble但仍允许将代码读取为“ ... sum”。


2
很好-我非常喜欢第二个-谢谢!我不了解上的新sum()方法Double
sparc_spread 2014年

6

这是另一种方法。如果你只需要一个列表上的求和,平均值,最小值,最大值等DoubleInteger或者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

注意,方法名称为summingDouble,还有另一个名为summarizingDouble的方法,该方法返回DoubleSummaryStatistics,其中包含所有基本的数学运算结果。


有没有办法只分配一个流同时分配平均值,最小值,最大值和总和的值?(对不起,我对lambdas不好)
jpganz18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.