Java 8 Stream reduce()

1. 概述

在本文中,我们将介绍Stream的reduce()方法。

归约操作允许我们通过对流中的元素重复应用合并操作, 从而产生一个单一结果。

比如,求和、求最大值或最小值、求出元素总个数、将所有元素转换成一个列表或集合。

2. 关键概念

在使用reduce()方法之前,先介绍三个概念。

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

3.快速上手

关于reduce()方法,Stream提供了三个重载的方法。 我们下面来分别的看一下。

3.1 使用累加器

BinaryOperator作为累加器传递给reduce()方法。 如果BinaryOperator的类型是数字,则起始值为0。 如果是字符串,则起始值为空白字符串。

Optional<T>	reduce​(BinaryOperator<T> accumulator)

这个方法将会返回Optional对象。 我们来看一个下面的示例:

int[] array = {1,2,3,4,5};
Arrays.stream(array).reduce((x, y) -> x+y).ifPresent(s -> System.out.println(s));
Arrays.stream(array).reduce(Integer::sum).ifPresent(s -> System.out.println(s));
Arrays.stream(array).reduce(StreamReduceTest::addIntData).ifPresent(s -> System.out.println(s));

输出如下:

15
15
15

需要注意的是:我们传递reduce()方法的累加器需要具有关联性(Associativity)。 如果满足以下条件,就可以认为运算符或函数op是具有关联性:

(a op b) op c == a op (b op c)

如果将元素的个数增加到为四个,就可以看出关联性对并行的重要性:

a op b op c op d == (a op b) op (c op d)

因此,我们可以将(c op d)和(a op b)并行执行,然后在结果上调用op。 一些创建的关联性操作有数字加法,最小值和最大值以及字符串连接。

3.2 使用标识和累加器

我们还可以将标识和累加器一起传给reduce()方法。 将标识作为初始值。

T reduce​(T identity, BinaryOperator<T> accumulator)

我们还是使用刚才的示例,这里我们将初始值设置为100。

int[] array = {1,2,3,4,5};
int start = 100;
int sum =  Arrays.stream(array).reduce(start, (x, y) -> x+y);
System.out.println(sum);
sum = Arrays.stream(array).reduce(start, Integer::sum);
System.out.println(sum);
sum =  Arrays.stream(array).reduce(start, StreamReduceTest::addIntData);
System.out.println(sum);

除了函数的关联性之外,需要注意的是:

  • 标识的类型需要和累加器处理的类型保持一致。
  • 使用标识和累加之后,reduce()方法返回的不再是Optional对象了。

3.3 使用标识,累加器和合并器

我们还可以将标识和累加器以及合并器一起传给reduce()方法。 将标识作为初始值。

<U> U reduce(U identity,
             BiFunction<U,? super T,U> accumulator,
             BinaryOperator<U> combiner)

合并器仅适用于并行流,否则没有任何内容可以合并。

而且合并器需要满足下面的条件:

// identity 标识 初始值
// accumulator 累加器
// combiner 合并器
combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

我们还是使用刚才的示例,

List<Integer> array = Arrays.asList(1,2,3,4,5);
int start = 100;
int sum =  array.parallelStream().reduce(start, (x, y) -> x + y, (p, q) -> p + q);
System.out.println(sum);
start = 0;
int sum2 =  array.parallelStream().reduce(start, (x, y) -> x + y, (p, q) -> p + q);
System.out.println(sum2);

输出如下:

515
15

我们从输出看出来,当我们的初始值为 100的时候,合并器并不满足我们上面提到的条件。在合并结果的时候,每一次都加了一下初始值。

4. 总结

在本文中,我们将介绍Stream的reduce()方法。并简单的介绍了reduce的重载方法。

与往常一样,可以在GitHub上获得代码示例。