简体   繁体   English

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

[英]Is mapToDouble() really necessary for summing a List<Double> with Java 8 streams?

As far as I can tell, the way to sum a List<Double> using Java 8 streams is this: 据我所知,使用Java 8流对List<Double>求和的方法是这样的:

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

To me, the mapToDouble(Double::doubleValue) seems kind of crufty - just the sort of boilerplate "ceremony" that lambdas and streams were supposed to dispense with. 对我来说, mapToDouble(Double::doubleValue)似乎有点夸张 - 只是那种lambdas和溪流应该免除的样板“仪式”。

Best practices tell us to prefer List instances over arrays, and yet for this sort of summing, arrays seem cleaner: 最佳实践告诉我们更喜欢List实例而不是数组,但是对于这种求和,数组似乎更清晰:

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

Granted, one could do this: 当然,人们可以做到这一点:

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

But that reduce(....) is so much longer than sum() . 但是reduce(....)sum()长得多。

I get that this has to do with the way streams need to be retrofitted around the Java's non-object primitives, but still, am I missing something here? 我知道这与流需要围绕Java的非对象原语进行改造的方式有关,但是,我在这里遗漏了一些东西吗? Is there some way to squeeze autoboxing in to make this shorter? 有没有办法挤压自动装箱以缩短它? Or is this just the current state of the art? 或者这仅仅是当前最先进的技术?


Update - Answers Digest 更新 - 答案摘要

Here is a digest of answers below. 以下是答案的摘要。 While I have a summary here, I urge the reader to peruse the answers themselves in full. 虽然我在这里有一个摘要,但我敦促读者自己仔细阅读答案。

@dasblinkenlight explains that some kind of unboxing will always be necessary, due to decisions made further back in the history of Java, specifically in the way generics were implemented and their relationship to the non-object primitives. @dasblinkenlight解释说,由于Java历史上的进一步决策,特别是在实现泛型的方式以及它们与非对象原语的关系中,因此总是需要某种拆箱。 He notes that it is theoretically possible for the compiler to intuit the unboxing and allow for briefer code, but this has not yet been implemented. 他指出,理论上编译器可以直接进行拆箱并允许使用简短的代码,但这还没有实现。

@Holger shows a solution that is very close to the expressiveness I was asking about: @Holger展示了一个非常接近我所询问的表现力的解决方案:

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

I was unaware of the new static Double.sum() method. 我不知道新的静态Double.sum()方法。 Added with 1.8, it seems intended for the very purpose I was describing. 添加1.8,似乎是出于我描述的目的。 I also found Double.min() and Double.max() . 我还找到了Double.min()Double.max() Going forward, I will definitely use this idiom for such operations on List<Double> and similar. 展望未来,我肯定会在List<Double>上使用这个成语进行此类操作。

Is there some way to squeeze autoboxing in to make this shorter? 有没有办法挤压自动装箱以缩短它?

Yes, there is. 就在这里。 You can simply write: 你可以简单地写:

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

This makes the unboxing implicit but, of course, does not add to efficiency. 这使得取消装箱是隐含的,但当然不会增加效率。

Since the List is boxed, unboxing is unavoidable. 由于List 装箱,因此拆箱是不可避免的。 An alternative approach would be: 另一种方法是:

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

It does not do a mapToDouble but still allows reading the code as “… sum”. 它不会执行mapToDouble但仍允许将代码读作“... sum”。

To me, the mapToDouble(Double::doubleValue) seems [what] lambdas and streams were supposed to dispense with. 对我来说, mapToDouble(Double::doubleValue)似乎是什么lambda和溪流应该免除。

The need to use mapToDouble is a consequence of a decision to implement generics via type erasure, essentially closing the door on any possibility of using primitives inside generics. 使用mapToDouble的需要是决定通过类型擦除实现泛型的结果,基本上关闭了在泛型中使用原语的任何可能性。 It is that same decision that made it necessary to create the DoubleStream , IntStream , and LongStream family of classes - to provide a stream-based unboxing. 正是这一决定使得必须创建DoubleStreamIntStreamLongStream类系列 - 以提供基于流的拆箱。

Is there some way to squeeze autoboxing in to make this shorter? 有没有办法挤压自动装箱以缩短它? Or is this just the current state of the art? 或者这仅仅是当前最先进的技术?

Unfortunately, not at this time: although it is theoretically possible for the compiler to figure out that Stream<Double> can be converted to DoubleStream implicitly, in the same way that the primitives are unboxed, this has not been done. 不幸的是,不是在这个时候:虽然理论上编译器可以理解Stream<Double>可以隐式地转换为DoubleStream ,但是与原始文件被取消装箱的方式相同,这还没有完成。

As far as your array-based solution goes, it is the most efficient of the three. 就基于阵列的解决方案而言,它是三者中效率最高的解决方案。 However, it is not as flexible as the other two: the one with mapToDouble lets you sum any attribute of a custom class, while the last one lets you perform other types of aggregation. 但是,它不像其他两个一样灵活:使用mapToDouble允许您对自定义类的任何属性求和,而最后一个允许您执行其他类型的聚合。

reduce(....) is so much longer than sum() reduce(....)sum()长得多

I agree, this approach is worse than mapToDouble in terms of readability. 我同意,这种方法在可读性方面比mapToDouble差。

Here is another way to do it. 这是另一种方法。 If you just need a sum, average, min, max etc. on a list of Double , Integer or Long , you can use one of the available Collectors , eg: 如果您只需要DoubleIntegerLong列表中的sum,average,min,max等,则可以使用其中一个可用的Collectors ,例如:

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

would print 18.41 会打印18.41

Note, the method name is summingDouble , there is another method called summarizingDouble , which returns DoubleSummaryStatistics , containing all the basic math operations results. 注意,方法名称是summingDouble ,还有另一个名为summarizingDouble的方法,它返回DoubleSummaryStatistics ,包含所有基本的数学运算结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM