reduce()方法在Java 8中如何工作?

21 java reduce java-8 java-stream

我试图了解该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); 
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是这样的:

Accumulator, a = 0, b = lorem
Accumulator, a = 5, b = ipsum
Accumulator, a = 10, b = sit
Accumulator, a = 13, b = amet
17
Run Code Online (Sandbox Code Playgroud)

它是这些字符串长度的总和。而且我看到没有访问组合器,因此它不会与数字相乘,只会将数字相加。

但是,如果我有这些信息流:

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); 
Run Code Online (Sandbox Code Playgroud)

这是输出:

Accumulator, a = 0, b = ipsum
Accumulator, a = 0, b = lorem
Accumulator, a = 0, b = sit
Combiner
Accumulator, a = 0, b = amet
Combiner
Combiner
300
Run Code Online (Sandbox Code Playgroud)

我看到累加器和合并器都被访问了,但是只有乘法才返回。那么总和会怎样?

Eug*_*ene 18

您应该阅读说明文档reduce

此外,组合器功能必须与累加器功能兼容;对于所有u和t,必须满足以下条件:

Combiner.apply(u,accumulator.apply(identity,t))==累加器.apply(u,t)

在你的情况,你打破了法律(做一个总和accumulator乘法combiner),所以你对这样的操作看,真是不定,依赖于Spliterator的底层源代码是如何实现的结果(不这样做!)。

此外,combiner要求并行流。

当然,您的整个方法可以简化为:

Arrays.asList("lorem", "ipsum", "sit", "amet")
      .stream()
      .mapToInt(String::length)
      .sum();
Run Code Online (Sandbox Code Playgroud)

如果您仅出于学习目的而这样做,则正确的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;
            });
Run Code Online (Sandbox Code Playgroud)


Dea*_*ool 12

关键概念:身份,累加器和组合器

Stream.reduce()操作:让我们将操作的参与者元素分解为单独的块。这样,我们将更容易理解每​​个人扮演的角色

  • 标识 –一个元素,它是归约运算的初始值,如果流为空,则为默认结果
  • itemAccumulator –具有两个参数的函数:归约运算的部分结果和流的下一个元素
  • 合并器 –一个具有两个参数的函数:归约运算的部分结果和流的下一个元素合并器– 此函数用于在归约化并行化时,或者当归约化之间不匹配时,合并归约运算的部分结果。累加器参数的类型和累加器实现的类型

当流并行执行时,Java运行时会将流拆分为多个子流。在这种情况下,我们需要使用一种函数将子流的结果合并为一个。这就是组合器的作用

情况1:parallelStream如您的示例所示,Combiner与

情况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());
Run Code Online (Sandbox Code Playgroud)

编译错误

The method reduce(User, BinaryOperator<User>) in the type Stream<User> is not applicable for the arguments (int, (<no type> partialAgeResult, <no type> user) -> {})
Run Code Online (Sandbox Code Playgroud)

我们可以通过使用Combiner来解决此问题:它是方法参考Integer::sum或使用lambda表达式(a,b)->a+b

int computedAges = users.stream().reduce(0, (partialAgeResult, user) -> partialAgeResult + user.getAge(),Integer::sum);
Run Code Online (Sandbox Code Playgroud)

简而言之,如果我们使用顺序流并且累加器参数的类型和其实现的类型匹配,则无需使用组合器。


Nik*_*las 8

有3种减少使用。简而言之,Stream::reduce从两个后续项(或与第一项相同的标识值)开始,并执行一项操作以产生新的折减值。对于每个下一项,都会发生同样的情况,并以减小的值执行操作。

比方说,你有流'a''b''c''d'。约简执行以下操作序列:

  1. result = operationOn('a', 'b')- operationOn可能是任何东西(输入长度​​的总和。)
  2. result = operationOn(result, 'c')
  3. result = operationOn(result, 'd')
  4. result is returned

方法是:

  • 好吧,术语“身份价值”已经是一个单独的术语,没有更好的替代品。“初始”或“默认”都不足够。身份值的约定已在文档中进行了解释,但是,正如[此答案](/sf/answers/2300709841/)中所述,它是一个术语,而不仅仅是为归约操作而发明的。 (2认同)

glg*_*lgl 2

我假设您选择进行加法和乘法作为演示,看看到底会发生什么。

正如您已经注意到的,并且正如已经提到的,组合器仅在并行流上被调用。

简而言之,在并行流上,流的一部分(分别是底层的 Spliterator)被切断并由不同的线程处理。处理多个部分后,它们的结果将通过组合器组合。

在您的情况下,这四个元素都由不同的线程处理,然后按元素进行组合。这就是为什么您看不到任何加法(除了0 +)被应用,而只有乘法。

然而,为了获得有意义的结果,您应该从 切换*+并执行更有意义的输出。