public static <T> Collector<T,?,Optional<T>> reducing(BinaryOperator<T> op)
不能看方法名理解其作用,入参是一个二目运算函数参数,作用比较广泛。
处理流程简单描述:
第一步;a,b两个参数先分别赋值集合中的第一个和第二个元素,经过计算的结果赋值给a。
第二步;取集合中第三个元素赋值给b,然后再对a,b两个参数进行计算,计算结果还是赋值给a。
第三步;重复第二步,直到遍历完全部集合元素。
第四步;把最终结果放到Optional中返回。
看一个例子:
void test34() {
List<Integer> list = List.of(2,5,8,9,4,20,11,43,55);
Optional<Integer> sum = list.stream().collect(Collectors.reducing((a,b) -> a+b));
System.out.println(sum.get());
}
运行结果:157
最后再强调一下,这个方法作用非常广泛功能非常强大。特别是配合groupingBy或partitioningBy使用简直是非常的好用。
看个例子:
void test35() {
List<User> userList = List.of((new User("mail1","adr1")),(new User("mail2","a2")),(new User("mail2","adr3")),(new User("mail1","adrr4")));
Map<String,Optional<User>> map = userList.stream().collect(Collectors.groupingBy(a -> a.getEmail(), Collectors.reducing((b,c) -> b.getAddr().length()<c.getAddr().length()?b:c)));
map.forEach((a,b) -> System.out.println(a+" : "+b.get().getAddr()));
}
运行结果:
mail2 : a2
mail1 : adr1
简单说一下代码的作用,就是以mail字段分组,分组结果取addr字段中字符串长度最小的User。
这个需求要是搁在java8之前去实现可以说绝对不可能一行代码就能解决,java8之前实现的思路基本上是这样,先对集合进行遍历,分别把相同mail的user放在同一个数组当中,再以mail为key,存放相同mail的user数组为value放到map当中,然后再遍历map中的value取出addr长度最小user保存到Optional中,最后以mail为key,最后一步得到的Optional为value放到map中返回。
public static <T> Collector<T,?,T> reducing(T identity, BinaryOperator<T> op)
在上个例子函数参数的两个入参初始化的时候是取集合中头两个元素,然后把计算结果赋值给第一个参数,第二个参数继续取集合中第三个元素然后使用这两个参数进行计算,以此类推完成整个集合元素的计算。在这个两个参数的reducing方法中比上个方法多了一个identity参数,这个参数的作用就是用来初始化第二个函数参数中的第一个入参的。计算流程简述如下:第一次计算中两个入参的第一个入参赋值identity值,第二个入参取集合中第一元素,然后进行计算,计算结果赋值第一个参数,第二个参数继续取集合中的第二个元素进行计算,以此类推,完成整个集合元素的计算。
看一个例子:
void test36() {
List<Integer> list = List.of(2,5,8,9,4,20,11,43,55);
Integer sum2 = list.stream().collect(Collectors.reducing(1, Integer::sum));
System.out.println(sum2);
}
运行结果是:158
对比一下上一个reducing方法,其差异就是二目运算函数中的两个入参第一次计算的时候初始赋值方法不一样,后面计算的处理流程一致。
// reducing 单参数和两个参数方法的区别
void test37() {
List<Integer> list = List.of(2,5,8,9,4,20,11,43,55);
// 模拟单参数reducing,其中 a第一次取的是list中的第一个元素,之后a取的都是a,b计算结果
int a=0,b=0;
boolean c = true;
for(int i: list) {
if(c) {a = i; c = false;}
else b = i;
a += b;
}
System.out.println(a); // 157
// 模拟2参数reducing, 其中a是在循环之前赋值,在reduceing中就是把第一个参数值赋给a,之后a都是取值a,b计算结果。
a=1;
for(int i: list) {
b = i;
a += b;
}
System.out.println(a); // 158
}
public static <T,U> Collector<T,?,U> reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)
三个参数的reducing,结合前面两个方法可以很容易理解这个方法的作用,其实这个方法和两个参数方法作用差不多,多了的第二个参数作用就是我们可以对取出的元素在使用第三个参数函数进行计算前先进行处理。两个参数的reducing就只能把从集合中取出的元素原原本本的放到第三个参数函数中进行计算。
看一个示例:
void test38() {
List<Integer> list = List.of(2,5,8,9,4,20,11,43,55);
Integer sum2 = list.stream().collect(Collectors.reducing(0, a->1, Integer::sum));
System.out.println(sum2);
}
运行结果:9
这里例子其实就是利用reducing方法求集合中的元素数量。如果把第二个参数修改为 a->a,那其作用还是求和,运行结果157
在看一个例子:
void test39() {
List<User> userList = List.of((new User("mail1","adr1")),(new User("mail2","a2")),(new User("mail2","adr3")),(new User("mail1","adrr4")));
String str = userList.stream().collect(Collectors.reducing("adr: ", a->a.getAddr(), (c,d) -> c+d+","));
System.out.println(str);
}
运行结果: adr: adr1,a2,adr3,adrr4,
代码的作用就是把集合中所有的user地址列出来。可以看到我们使用第二个参数先从user对象中取出地址,然后再放到第三个参数函数中计算,当然我们使用两个函数的reducing也能做到,只是取user中地址的操作就必须放到第三个参数函数里进行了。