我们先来翻下api:
Optional<T> reduce(BinaryOperator<T> accumulator) :Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any. T reduce(T identity, BinaryOperator<T> accumulator) :Performs a reduction on the elements of this stream, using the provided identity value and an associative accumulation function, and returns the reduced value. <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner):Performs a reduction on the elements of this stream, using the provided identity, accumulation and combining functions.
先来看一段代码演示下如果对一个流做缩减操作:
public static void main(String[] args) throws Exception { List<Integer> list = new ArrayList<>(4); list.add(1); list.add(2); list.add(3); list.add(4); //reduce第一个重载,直接进行缩减 list.stream().reduce((a, b) -> a + b).ifPresent(System.out::println); //reduce第二个重载,可以指定初始值,然后在进行缩减 System.out.println(list.stream().reduce(0, Integer::sum)); //reduce第三个重载,可以指定初始值,然后在进行缩减,第3个参数用来处理并行流的操作 System.out.println(list.stream().reduce(0, (a, b) -> a * 2 + b, (c, d) -> c + d)); List<String> list1 = Lists.newArrayList("1", "2", "3", "4"); System.out.println(list1.stream().reduce(1, (a, b) -> a + b.length(), (c, d) -> c + d)); }
这里我们重点要看下前面2个方法:
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
第一个方法返回一个Optional对象,该对象包含了结果。第二个方法直接返回T类型的对象,这里的这个T类型就是流中元素的类型。在使用这2个方法的时候,要传入一个Lambda表达式,这个表达式要实现BinaryOperator<T>函数式接口,先说下这个接口吧:具体的可以去看我前面有篇博客的,这些函数式接口很容易忘掉了,我晕。
public interface BinaryOperator<T> extends BiFunction<T,T,T> {}
这个函数式接口扩展了BiFunction<T,T,T> 接口,这里只不过赋值方法的参数是同一个类型,然后返回值也是同一个类型而已。对于,BinaryOperator<T> 来说,apply方法现在变成了如下方法:
T apply(T val, T value);其中val将包含前一个结果,然后value包含下一个元素。我们在调用reduce的时候,val将包含单位初始值或者第一个元素。在上面2个方法中,第一个方法将包含第一个元素,第二个方法将包含单位值。
注意:
累加器操作必须满足以下3个约束:
1,无状态
2,不干预
3,结合性
无状态意味着操作不依赖于任何状态信息,不干预意味着操作不会改变数据源,最后操作必须具有关联性。这里的关联性可以理解为加法结合律或者乘法结合律,举个例子,(a+b)+c=a+(b+c);
最后这里整理下reduce的第3个重载方法:
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)。
考虑如下情景:假设我们现在有一个包含多个对象的流,并且希望对他们的某个属性进行求和。例如求一个流中的所有字符串的总长度,这个时候我们没有办法用上面的2个reduce的重载,因为上面2个方法中需要一个(T,T)->T这样子的函数式接口,但是这里这2个类型是不同的,流中的元素是String的,但是累加的结果是整型的。对于这种情况,我们只能使用重载的第3个方法了。
public static void main(String[] args) { Stream<String> of = Stream.of("张飞", "关羽"); //一下代码报错,类型不匹配The method reduce(String, BinaryOperator<String>) in the type Stream<String> //is not applicable for the arguments (int, BinaryOperator<String>) of.reduce(0, (a, b) -> a + b.length()); System.out.println(of.reduce(0, (a, b) -> a + b.length(), Integer::sum)); }