Java 8 reduce()方法在Java8中是如何工作的?
我试图理解Java 8 reduce()方法在Java8中是如何工作的?,java-8,java,java-stream,reduce,Java 8,Java,Java Stream,Reduce,我试图理解reduce()方法是如何工作的 例如,我有以下代码: 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(
reduce()
方法是如何工作的
例如,我有以下代码:
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);
}
}
它是这些字符串长度的总和。我看到合并器没有被访问,所以它不会对数字进行乘法,它只对数字进行加法
但是如果我将流
替换为并行流
:
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
我看到累加器和合并器都被访问,但只有乘法返回。那么求和会发生什么呢?我假设您选择做加法和乘法只是为了演示一下到底发生了什么 正如您已经注意到的,正如前面提到的,合并器只在并行流上被调用 简而言之,在并行STRAM上,流的一部分(即底层拆分器)被切断,并由不同的线程处理。在对几个部分进行处理之后,它们的结果将与组合器合并 在您的例子中,这四个元素都由不同的线程处理,然后按元素进行组合。这就是为什么你看不到任何加法(除了
0+
)被应用,而只看到乘法
但是,为了获得有意义的结果,您应该从
*
切换到+
,而不是执行更有意义的输出。您应该阅读reduce
的文档,其中说明:
此外,组合器功能必须与累加器功能兼容;对于所有u和t,必须满足以下条件:
组合器.apply(u,累加器.apply(identity,t))==累加器.apply(u,t)
在您的例子中,您违反了这条定律(在累加器中进行求和,在组合器中进行乘法),因此您看到的这种操作的结果实际上是未定义的,并且取决于底层源的拆分器是如何实现的(不要这样做!)
此外,组合器
仅用于并行流
当然,您的整个方法可以简化为:
Arrays.asList("lorem", "ipsum", "sit", "amet")
.stream()
.mapToInt(String::length)
.sum();
如果您这样做只是为了学习,那么正确的减少将是(为了获得总和
):
关键概念:标识、累加器和组合器
The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> {})
Stream.reduce()操作:让我们将操作的参与者元素分解为单独的块。这样,我们就可以更容易地理解每个人所扮演的角色
- 标识–一个元素,是还原操作的初始值,如果流为空,则为默认结果
- 项累加器–一个采用两个参数的函数:还原操作的部分结果和流的下一个元素
- 组合器–采用两个参数的函数:还原操作的部分结果和流的下一个元素
Combiner–一种函数,用于在还原被并行化时,或当累加器参数类型与累加器实现类型不匹配时,合并还原操作的部分结果
当一个流并行执行时,Java运行时将该流拆分为多个子流。在这种情况下,我们需要使用一个函数将子流的结果合并为一个结果。这是组合器的角色
案例1:组合器与parallelStream
一起工作,如示例所示
案例2:具有不同类型参数的累加器示例
在本例中,我们有一个用户对象流,累加器参数的类型是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());
简单地说,如果我们使用顺序流和累加器参数的类型及其实现匹配的类型,我们不需要使用组合器。有3种方法可以减少使用。简而言之,Stream::reduce
从两个后续项(或一个标识值,其中一个包含第一个)开始,并使用它们执行一个操作,生成新的缩减值。对于下一个项目,同样的情况也会发生,并使用减少的值执行操作
假设您有一个'a'
,'b'
,'c'
和'd'
流。还原执行以下操作序列:
result=operationOn('a','b')
-operationOn
可以是任何东西(输入长度的总和..)
result=operation(结果'c')
result=operation(结果'd')
返回结果
这些方法是:
- 对元素执行缩减。开始时,前两个项目产生一个缩减值,然后每个项目产生一个缩减值。由于不能保证输入流不为空,因此返回
可选的
- 执行与上述方法相同的操作,但标识值作为第一项给出。返回
T
,因为由于T标识
,始终至少有一个项目得到保证
- 执行与上述方法相同的操作,并添加一项功能组合。返回
U
,因为由于U标识
,始终保证至少有一个项目
组合器
仅针对并行流调用。另外,请仔细阅读reduce的文档,您不能在累加器
中进行求和,在组合器
中进行乘法,并期望得到一些有意义的结果将标识值称为“默认值”是误导性的,甚至更糟。如果您想要一个默认值
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) -> {})
int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);