[英]How does the reduce() method work in Java 8?
我试图了解reduce()
方法在java-8中是如何工作的。
例如我有这个代码:
public class App {
public static void main(String[] args) {
String[] arr = {"lorem", "ipsum", "sit", "amet"};
List<String> strs = Arrays.asList(arr);
int ijk = strs.stream().reduce(0,
(a, b) -> {
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
},
(a, b) -> {
System.out.println("Combiner");
return a * b;
});
System.out.println(ijk);
}
}
输出是这样的:
Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
它是这些字符串长度的总和。 我看到组合器没有被访问,所以它不会乘以数字,它只会增加数字。
但是如果我用parallelStream
替换stream
:
int ijk = strs.parallelStream().reduce(0,
(a, b) -> {
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
},
(a, b) -> {
System.out.println("Combiner");
return a * b;
});
System.out.println(ijk);
这是输出:
Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
我看到累加器和组合器都被访问,但只有乘法返回。 那么总和是怎么回事呢?
你应该阅读reduce
的文档,它说:
此外,组合器功能必须与累加器功能兼容; 对于所有 u 和 t,以下必须成立:
combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
在您的情况下,您违反了该法律(在accumulator
进行求和并在combiner
乘法),因此您看到的此类操作的结果实际上是未定义的,并且取决于底层源的 Spliterator 是如何实现的(不要去做!)。
此外,仅对并行流调用combiner
。
当然,您的整个方法可以简化为:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
如果您只是为了学习目的而这样做,那么正确的reduce
将是(获得sum
):
strs.parallelStream()
.reduce(0,
(a, b) -> {
System.out.println("Accumulator, a = " + a + ", b = " + b);
return a + b.length();
},
(a, b) -> {
System.out.println("Combiner");
return a + b;
});
关键概念:恒等式、累加器和组合器
Stream.reduce() 操作:让我们将操作的参与者元素分解为单独的块。 这样,我们会更容易理解每个人所扮演的角色
当流并行执行时,Java 运行时将流拆分为多个子流。 在这种情况下,我们需要使用一个函数将子流的结果合并为一个。 这就是合路器的作用
案例 1:如您的示例所示,Combiner 与parallelStream
一起使用
案例 2:具有不同类型参数的示例累加器
在这种情况下,我们有一个 User 对象流,并且累加器参数的类型是 Integer 和 User。 但是,累加器实现是整数的总和,因此编译器无法推断用户参数的类型。
List<User> users = Arrays.asList(new User("John", 30), new User("Julie", 35));
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge());
编译错误
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> {})
我们可以通过使用组合器来解决这个问题:它是方法引用Integer::sum
或使用 lambda 表达式(a,b)->a+b
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
简单来说,如果我们使用顺序流并且累加器参数的类型与其实现的类型匹配,我们就不需要使用组合器。
有 3 种方法可以使用java-stream减少。 简而言之, Stream::reduce
以两个后续项目(或与第一个相同的身份值)开始,并使用它们执行操作以产生新的减少值。 对于每个下一个项目,同样的事情发生,并使用减少的值执行操作。
假设您有一个'a'
、 'b'
、 'c'
和'd'
。 归约执行以下操作序列:
result = operationOn('a', 'b')
- operationOn
可能是任何东西(输入长度的总和..)result = operationOn(result, 'c')
result = operationOn(result, 'd')
result is returned
方法是:
Optional<T> reduce(BinaryOperator<T> accumulator)
对元素执行缩减。 从产生减少值的前两个项目开始,然后是具有减少值的每个项目。 Optional<T>
被返回,因为它不能保证输入流不为空。
T reduce(T identity, BinaryOperator<T> accumulator)
与上面的方法相同,除了标识值作为第一项给出。 返回T
是因为总是有至少一项保证,因为T identity
。
U reduce(U identity, BiFunction<U,? super T, U> accumulator, BinaryOperator<U> combiner)
与上述方法相同,只是将函数组合在一起。 返回U
是因为始终至少保证一项,因为U identity
。
我假设你选择做加法和乘法只是作为一个演示来看看到底发生了什么。
正如您已经注意到的,并且正如已经提到的,组合器仅在并行流上被调用。
简而言之,在并行流上,流的一部分(对应于底层的 Spliterator)被截断并由不同的线程处理。 在处理了几个部分之后,它们的结果会被一个组合器组合起来。
在您的情况下,四个元素都由不同的线程处理,然后组合发生在元素方面。 这就是为什么您没有看到任何加法(除了0 +
)被应用,而只有乘法。
但是,为了获得有意义的结果,您应该从*
切换到+
,而是执行更有意义的输出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.