mapToDouble() 真的需要用 Java 8 流对 List<Double> 求和吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24421140/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is mapToDouble() really necessary for summing a List<Double> with Java 8 streams?
提问by sparc_spread
As far as I can tell, the way to sum a List<Double>
using Java 8 streams is this:
据我所知,List<Double>
使用 Java 8 流对 a 求和的方法是这样的:
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? Is there some way to squeeze autoboxing in to make this shorter? Or is this just the current state of the art?
我知道这与需要围绕 Java 的非对象原语改造流的方式有关,但是,我在这里遗漏了什么吗?有什么方法可以挤压自动装箱以使其更短吗?或者这只是目前的技术水平?
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. 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.
@dasblinkenlight 解释说,由于 Java 历史上更早的决定,特别是泛型的实现方式及其与非对象原语的关系,某种拆箱总是必要的。他指出,理论上编译器可以直观地拆箱并允许更简短的代码,但这尚未实现。
@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. Added with 1.8, it seems intended for the very purpose I was describing. I also found Double.min()
and Double.max()
. Going forward, I will definitely use this idiom for such operations on List<Double>
and similar.
我不知道新的静态Double.sum()
方法。添加了 1.8,它似乎是为了我所描述的目的。我还发现Double.min()
和Double.max()
。展望未来,我肯定会使用这个习语进行类似的操作List<Double>
。
采纳答案by dasblinkenlight
To me, the
mapToDouble(Double::doubleValue)
seems [what] lambdas and streams were supposed to dispense with.
对我来说,
mapToDouble(Double::doubleValue)
似乎 [什么] lambdas 和流应该被免除。
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. 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.
需要使用mapToDouble
是决定通过类型擦除实现泛型的结果,基本上关闭了在泛型中使用原语的任何可能性的大门。这是使得有必要创建相同的决定DoubleStream
,IntStream
和LongStream
家庭类-提供基于流的拆箱。
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.
就基于阵列的解决方案而言,它是三者中最有效的。但是,它不如其他两个灵活:一个 withmapToDouble
允许您对自定义类的任何属性求和,而最后一个则允许您执行其他类型的聚合。
reduce(....)
is so much longer thansum()
reduce(....)
比sum()
I agree, this approach is worse than mapToDouble
in terms of readability.
我同意,这种方法比mapToDouble
可读性差。
回答by Holger
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
isboxed, unboxing is unavoidable. An alternative approach would be:
由于List
是装箱的,拆箱是不可避免的。另一种方法是:
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”。
回答by jFrenetic
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
, e.g.:
这是另一种方法。如果您只需要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))
);
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
,包含所有基本的数学运算结果。