繁体   English   中英

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

[英]How does the reduce() method work in Java 8?

我试图了解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); 
    }
}

输出是这样的:

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() 操作:让我们将操作的参与者元素分解为单独的块。 这样,我们会更容易理解每​​个人所扮演的角色

  • Identity – 一个元素,它是归约操作的初始值,如果流为空则为默认结果
  • itemAccumulator – 一个接受两个参数的函数:减少操作的部分结果和流的下一个元素
  • 组合器-一个函数,它有两个参数:归约运算的部分结果和流组合器的下一个元素-用于将归约运算的部分结果结合时,减少被并行化,或一个功能时,有在之间的不匹配累加器参数的类型和累加器实现的类型

当流并行执行时,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 种方法可以使用减少。 简而言之, 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

方法是:

我假设你选择做加法和乘法只是作为一个演示来看看到底发生了什么。

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

简而言之,在并行流上,流的一部分(对应于底层的 Spliterator)被截断并由不同的线程处理。 在处理了几个部分之后,它们的结果会被一个组合器组合起来。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM