Java 8 Streams - 为什么我不能对整数流进行求和?

10
给定一个整数列表: List<Integer> numbers = Arrays.asList(1,2,3); 为什么不能像这样对它们求和:numbers.stream().sum();
相反,我必须这样做:numbers.stream().mapToInt(e -> e).sum(); 我知道mapToInt会产生一个基本类型的IntStream。但是我还是不理解为什么不能直接对整数求和。我知道这个列表里面都是整数,编译器应该可以做到同样的事情。毕竟,它现在可以在lambda表达式中推断类型参数。
好吧,可能有Integer是null,导致求和失败。但是我可以承担这个责任并过滤掉null: numbers.stream().filter(Objects::nonNull).sum(); 为什么我不能对整数流进行求和呢?

3
因为(类型擦除之后),它是一个 List<Object> - Elliott Frisch
9
Stream接口没有定义sum方法,因为它并不适用于所有流。通用解决方案是使用reduce方法。 - Denys Séguret
1
请参见此问题 - Holger
这让我想起了为什么没有Stream.flatMap()函数?(不幸的是已关闭),由于同样的原因,实际上也不可能。 - Didier L
4个回答

18

在一个List上调用stream()方法将获得一个通用的Stream,可以处理任何引用类型,而不仅仅是数值类型。将sum方法包含在Stream中没有意义,因为对于非数值参考类型(如URLClassThread或其他任何非数值参考类型),求和是没有意义的。

通过调用reduce方法可以对元素进行求和:

numbers.stream().reduce(0, (a, b) -> a + b)

但这将涉及大量的拆箱和装箱。最好是像你现在这样将它们总结起来,通过将其转换为操作intIntStream,并调用sum()(或summaryStatistics(),它包括计数、平均值、最大值和最小值以及总和)。

你甚至可以使用IntStream.of,避免装箱值一次。

IntStream.of(1, 2, 3).sum()

2
使用方法引用替换lambda表达式:numbers.stream().reduce(0, Math::addExact)(在溢出时失败)--- 或者,将Integer值拆箱:numbers.stream().mapToInt(Number::intValue).sum() - Andreas
1
我不建议仅使用summaryStatistics()来获取总和。这应该始终避免,而应该使用numbers.stream().mapToInt(e -> e).sum(); - Ousmane D.
4
mapToInt 版本实际上是最好的,因为它不会对所有中间总和进行装箱和拆箱操作。 - Andreas
4
@Andreas 或 .collect(Collectors.summingInt(i -> i)),这也避免了装箱操作。 - Holger
@Holger 有很多种方法可以解决这个问题 :-) - Andreas
显示剩余2条评论

4

Stream<Interger>IntStream 实际上是不同的。因为 numbers.stream() 返回类型为 Stream 的对象,而 Stream 接口并没有像 sum 一样的方法。另一方面,numbers.stream().mapToInt(e -> e) 返回 IntStream,它有 sum 方法。


3

您不能进行以下操作:

numbers.stream().sum();

由于Stream<T>是一个通用类型,就其而言,它正在处理任何类型的对象。例如,这可以是Stream<Person>Stream<Apple>等等。因此,包含一个sum方法是没有意义的。


2
我认为你所要求的事情根本无法实现,即使从理论上讲也不行。我的意思是,stream() 声明在 List 接口上并返回 Stream<E>。你知道 E 实际上是一个 Integer,编译器必须进行类型推断,但编译器不能在推断类型时更改返回类型,仍然会得到一个一般的 Stream<E>

但如果我再深入思考一下,情况会更糟。在这种情况下,必须返回一个 BaseStream,它既可以扩展 Stream<String>,也可以扩展 IntStream,而对于 Stream<String> 来说,sum 没有意义。稍后,必须在运行时保留 E 类型,以便知道要返回哪种特化类型,显然这是不可能的(除非有一个无用的证人)。

如果有一个 IntsList,一个理论上的 IntsArray.asIntsList 将返回该列表,则确实可以实现此功能,但在这种情况下,将变得非常混乱。


1
你认为"IntsList"会带来什么样的“混乱”?相比将java.util.stream的大部分功能三次镜像(IntStream,LongStream,DoubleStream),并将java.util.function的大多数函数三次镜像(IntBinaryOperator,LongBinaryOperator,DoubleBinaryOperator,IntUnaryOperator,LongUnaryOperator,DoubleUnaryOperator,IntPredicate,LongPredicate,DoublePredicate), 它会带来更大的混乱吗? - Holger
@Holger 很多很多的API应该会改变吧?在我看来这可能会是一种“混乱”,直到每个人都习惯了它,我猜。 - Eugene
1
也许你应该改进你回答的最后一部分,以明确你的意思。就目前而言,你正在谈论一个假设的 IntsList 类,其 API 与 java.util.List 类似,为了支持 stream().sum(),它只需要让其 stream() 方法返回一个 IntStream 即可。不清楚你对其他现有 API 的修改有哪些想法。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接