reducing和reduce

2022-07-04

java

我的悲伤还来不及出发,就已经到站下车。——《第七天》

java中,分为Collectors.reducingStream#reduce

reduce是减少的意思,此处意为聚合

聚合是聚拢、合并的意思

我们来看看这俩函数的区别吧,下方我用了静态导入:

1
2
3
4
5
6
7
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collector;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

首先是写法差异,对于只有一个参数的,这个参数指定了我们聚合的操作,此处我做一个累加,返回值为Optional证明有可能不存在值,就没有累加

1
2
3
4
Optional<Integer> sumOpt = Stream.iterate(0, i -> ++i).limit(10).collect(reducing(Integer::sum));
System.out.println(sumOpt); // Optional[45]
sumOpt = Stream.iterate(0, i -> ++i).limit(10).reduce(Integer::sum);
System.out.println(sumOpt); // Optional[45]

两个参数的,这里第一个参数为默认值,这里返回的是默认值+累加后的结果,此处默认值只能指定为相同类型

1
2
3
4
Integer sum = Stream.iterate(0, i -> ++i).limit(10).collect(reducing(10, Integer::sum));
System.out.println(sum); // 55
sum = Stream.iterate(0, i -> ++i).limit(10).reduce(10, Integer::sum);
System.out.println(sum); // 55

到此为止,其实都差不多,下面是三个参数的,这个三参用于聚合为其他类型的默认值,第一个参数还是默认值,第二个参数和第三个参数就有区别了

1
2
3
4
5
6
BigDecimal sumDecimal = Stream.iterate(0, i -> ++i).limit(10).collect(
reducing(BigDecimal.ZERO, BigDecimal::new, BigDecimal::add));
System.out.println(sumDecimal); // 45
sumDecimal = Stream.iterate(0, i -> ++i).limit(10)
.reduce(BigDecimal.ZERO, (d, i) -> d.add(new BigDecimal(i)), BigDecimal::add);
System.out.println(sumDecimal); // 45

可以看出我们的Collectors.reducing第二个参数是一个Function<Integer,BigDecimal>,入参为Integer返回值为BigDecimal,并没有进行聚合运算,而是进行了一个转换,此处是由Integer去生成一个BigDecimal,调用的java.math.BigDecimal#BigDecimal(int)这个构造,而第三个参数才是我们的累加操作

Stream#reduce中,第二个参数是一个BiFunction<BigDecimal, Integer, BigDecimal>,入参变为两个参数BigDecimal(已经累加的结果,并行流下值不可控)和Integer(本次参与运算的值),返回值为BigDecimal(运算结果),第三个参数是个BinaryOperator<BigDecimal>只在并行流场景下会用到,之前讲过,这里就不再表了,贴上链接:

reduce补充二

第三个参数区别:

也就是说,我们在串行流中哪怕将Stream#reduce的第三个参数,改为任意操作,他都是不影响结果执行的,例如我们这里取最大值

1
2
3
4
5
6
BigDecimal sumDecimal = Stream.iterate(0, i -> ++i).limit(10).collect(
reducing(BigDecimal.ZERO, BigDecimal::new, BinaryOperator.maxBy(BigDecimal::compareTo)));
System.out.println(sumDecimal); // 9
sumDecimal = Stream.iterate(0, i -> ++i).limit(10)
.reduce(BigDecimal.ZERO, (d, i) -> d.add(new BigDecimal(i)), BinaryOperator.maxBy(BigDecimal::compareTo));
System.out.println(sumDecimal); // 45

可以看出,哪怕我们改为BinaryOperator.maxBy(BigDecimal::compareTo),是不影响Stream#reduce的,哪怕我们改为null

1
2
3
4
5
6
BigDecimal sumDecimal = Stream.iterate(0, i -> ++i).limit(10).collect(
reducing(BigDecimal.ZERO, BigDecimal::new, (l, r) -> null));
System.out.println(sumDecimal); // null
sumDecimal = Stream.iterate(0, i -> ++i).limit(10)
.reduce(BigDecimal.ZERO, (d, i) -> d.add(new BigDecimal(i)), (l, r) -> null);
System.out.println(sumDecimal); // 45

除非并行流场景下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Optional<Integer> sumOpt = Stream.iterate(0, i -> ++i).parallel().limit(10).collect(reducing(Integer::sum));
System.out.println(sumOpt); // Optional[45]
sumOpt = Stream.iterate(0, i -> ++i).parallel().limit(10).reduce(Integer::sum);
System.out.println(sumOpt); // Optional[45]
Integer sum = Stream.iterate(0, i -> ++i).parallel().limit(10).collect(reducing(10, Integer::sum));
System.out.println(sum); // 145
sum = Stream.iterate(0, i -> ++i).parallel().limit(10).reduce(10, Integer::sum);
System.out.println(sum); // 145
BigDecimal sumDecimal = Stream.iterate(0, i -> ++i).parallel().limit(10).collect(
reducing(BigDecimal.ZERO, BigDecimal::new, (l, r) -> null));
System.out.println(sumDecimal); // null
sumDecimal = Stream.iterate(0, i -> ++i).parallel().limit(10)
.reduce(BigDecimal.ZERO, (d, i) -> d.add(new BigDecimal(i)), (l, r) -> null);
System.out.println(sumDecimal); // null

我们再次改为求最大值

1
2
3
4
5
6
BigDecimal sumDecimal = Stream.iterate(0, i -> ++i).parallel().limit(10).collect(
reducing(BigDecimal.ZERO, BigDecimal::new, BinaryOperator.maxBy(BigDecimal::compareTo)));
System.out.println(sumDecimal); // 9
sumDecimal = Stream.iterate(0, i -> ++i).parallel().limit(10)
.reduce(BigDecimal.ZERO, (d, i) -> d.add(new BigDecimal(i)), BinaryOperator.maxBy(BigDecimal::compareTo));
System.out.println(sumDecimal); // 9

可以看到并行流场景下均生效