Stream系列:

  • Java 8系列之Stream的基本语法详解
  • Java 8系列之Stream的强大工具Collector
  • Java 8系列之重构和定制收集器
  • Java 8系列之Stream中万能的reduce

reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。

reduce方法有三个override的方法:

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

我们先看第一个变形,其接受一个函数接口BinaryOperator<T>,而这个接口又继承于BiFunction<T, T, T>.在BinaryOperator接口中,又定义了两个静态方法minBy和maxBy。这里我们先不管这两个静态方法,先了解reduce的操作。

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {Objects.requireNonNull(comparator);return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;}public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {Objects.requireNonNull(comparator);return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;}
}

在使用时,我们可以使用Lambda表达式来表示

BinaryOperator接口,可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素。这个方法返回值类型是Optional,

Optional accResult = Stream.of(1, 2, 3, 4).reduce((acc, item) -> {System.out.println("acc : "  + acc);acc += item;System.out.println("item: " + item);System.out.println("acc+ : "  + acc);System.out.println("--------");return acc;});
System.out.println("accResult: " + accResult.get());
System.out.println("--------");
// 结果打印
--------
acc : 1
item: 2
acc+ : 3
--------
acc : 3
item: 3
acc+ : 6
--------
acc : 6
item: 4
acc+ : 10
--------
accResult: 10
--------

下面来看第二个变形,与第一种变形相同的是都会接受一个BinaryOperator函数接口,不同的是其会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值。另一方面,该方法不会返回Optional,因为该方法不会出现null。

int accResult = Stream.of(1, 2, 3, 4).reduce(0, (acc, item) -> {System.out.println("acc : "  + acc);acc += item;System.out.println("item: " + item);System.out.println("acc+ : "  + acc);System.out.println("--------");return acc;});
System.out.println("accResult: " + accResult);
System.out.println("--------");
// 结果打印
acc : 0
item: 1
acc+ : 1
--------
acc : 1
item: 2
acc+ : 3
--------
acc : 3
item: 3
acc+ : 6
--------
acc : 6
item: 4
acc+ : 10
--------
accResult: 10
--------

从打印结果可以看出,reduce前两种变形,因为接受参数不同,其执行的操作也有相应变化:

变形1,未定义初始值,从而第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
变形2,定义了初始值,从而第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素
对于第三种变形,我们先看各个参数的含义,第一个参数返回实例u,传递你要返回的U类型对象的初始化实例u,第二个参数累加器accumulator,可以使用二元?表达式(即二元lambda表达式),声明你在u上累加你的数据来源t的逻辑,例如(u,t)->u.sum(t),此时lambda表达式的行参列表是返回实例u和遍历的集合元素t,函数体是在u上累加t,第三个参数组合器combiner,同样是二元?表达式,(u,t)->u。在官方文档上有这么一段介绍,

U result = identity;

for (T element : this stream)

result = accumulator.apply(result, element)

return result;

but is not constrained to execute sequentially.

The identity value must be an identity for the combiner function. This means that for all u, combiner(identity, u) is equal to u. Additionally, the combiner function must be compatible with the accumulator function; for all u and t, the following must hold:

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

因为reduce的变形的第一个参数类型是实际返回实例的数据类型,同时其为一个泛型也就是意味着该变形的可以返回任意类型的数据。从上面文档介绍的字面意思解读是第三个参数函数用来组合两个值,而这两个值必须与第二个函数参数相兼容,也就是说它们所得的结果是一样的。看到这里肯定有迷惑的地方,第三个参数到底是用来干嘛的?我们先看一段代码,为了便于了解其中的缘由,并没有使用Lambda表达式,

ArrayList<Integer> accResult_ = Stream.of(1, 2, 3, 4).reduce(new ArrayList<Integer>(),new BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>>() {@Overridepublic ArrayList<Integer> apply(ArrayList<Integer> acc, Integer item) {acc.add(item);System.out.println("item: " + item);System.out.println("acc+ : " + acc);System.out.println("BiFunction");return acc;}}, new BinaryOperator<ArrayList<Integer>>() {@Overridepublic ArrayList<Integer> apply(ArrayList<Integer> acc, ArrayList<Integer> item) {System.out.println("BinaryOperator");acc.addAll(item);System.out.println("item: " + item);System.out.println("acc+ : " + acc);System.out.println("--------");return acc;}});
System.out.println("accResult_: " + accResult_);
// 结果打印
item: 1
acc+ : [1]
BiFunction
item: 2
acc+ : [1, 2]
BiFunction
item: 3
acc+ : [1, 2, 3]
BiFunction
item: 4
acc+ : [1, 2, 3, 4]
BiFunction
accResult_: [1, 2, 3, 4]
accResult_: 10

首先示例代码中,传递给第一个参数是ArrayList,在第二个函数参数中打印了“BiFunction”,而在第三个参数的函数接口中打印了”BinaryOperator“.可是,看打印结果,只是打印了“BiFunction”,而没有打印”BinaryOperator“,说明第三个函数参数并没有执行。这里我们知道了该变形可以返回任意类型的数据。对于第三个函数参数,为什么没有执行,刚开始的时候也是没有看懂到底是啥意思呢,而且其参数必须为返回的数据类型?看了好几遍文档也是一头雾水。在 java8 reduce方法中的第三个参数combiner有什么作用?这里找到了答案,Stream是支持并发操作的,为了避免竞争,对于reduce线程都会有独立的result,combiner的作用在于合并每个线程的result得到最终结果。这也说明了了第三个函数参数的数据类型必须为返回数据类型了。

需要注意的是,因为第三个参数用来处理并发操作,如何处理数据的重复性,应多做考虑,否则会出现重复数据!
--------------------- 
作者:行云间 
来源:CSDN 
原文:https://blog.csdn.net/IO_Field/article/details/54971679 
版权声明:本文为博主原创文章,转载请附上博文链接!

Java 8系列之Stream中万能的reduce相关推荐

  1. java reduce的用法_Java 8系列之Stream中万能的reduce用法说明

    reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型.比如,之前提到count.min和max方法,因为常用而被纳入标准库中.事实上,这些方法都是redu ...

  2. java8 reduce的用法_Java 8系列之Stream中万能的reduce用法说明

    reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型.比如,之前提到count.min和max方法,因为常用而被纳入标准库中.事实上,这些方法都是redu ...

  3. Java 8系列之Stream的强大工具Collector

    Stream系列: Java 8系列之Stream的基本语法详解 Java 8系列之Stream的强大工具Collector Java 8系列之重构和定制收集器 Java 8系列之Stream中万能的 ...

  4. 【Java 8系列】Stream详解,看这一篇就够啦

    热门系列: [Java 8系列]收集器Collector与工具类Collectors [Java 8系列]Lambda 表达式,一看就废 [Java 8系列]Java日期时间的新主宰者:LocalDa ...

  5. java skip函数_【Java必修课】图说Stream中的skip()和limit()方法及组合使用

    1 简介 本文将讲解Java 8 Stream中的两个方法:skip()和limit().这两个方法是Stream很常用的,不仅各自会被高频使用,还可以组合出现,并能实现一些小功能,如subList和 ...

  6. Effective Java~46. 优先选择Stream 中无副作用的函数

    纯函数(pure function)的结果仅取决于其输入:它不依赖于任何可变状态,也不更新任何状态. 坏味道 // Uses the streams API but not the paradigm- ...

  7. Java 8系列之重构和定制收集器

    Stream系列: Java 8系列之Stream的基本语法详解 Java 8系列之Stream的强大工具Collector Java 8系列之重构和定制收集器 Java 8系列之Stream中万能的 ...

  8. 【Java 8系列】Java开发者的判空利器 -- Optional

    热门系列: [Java 8系列]收集器Collector与工具类Collectors [Java 8系列]Stream详解,看这一篇就够啦 [Java 8系列]Lambda 表达式,一看就废 [Jav ...

  9. 【Java 8系列】Java日期时间的新主宰者:LocalDate、LocalTime、LocalDateTime、ZonedDateTime

    热门系列: [Java 8系列]收集器Collector与工具类Collectors [Java 8系列]Stream详解,看这一篇就够啦 [Java 8系列]Lambda 表达式,一看就废 [Jav ...

最新文章

  1. POJ 1426 Find The Multiple
  2. 视频分享:编码与代码评审-质量与现实的最激烈冲突点(完整版)
  3. Spark在不同集群中的运行架构
  4. Shell命令-搜索文件或目录之whereis、locate
  5. e - bargain cf_cf诗意情侣名字大全最新_cf诗意情侣名字大全2020
  6. 【pmcaff专栏】陆蔚青:漫谈商业智能
  7. C++ Function语意学
  8. java包引入顺序_多个相同jar存在时的引用顺序
  9. 使用CloudForms实现云运营幸福感的3个步骤
  10. 正则仅输入文字字母空格,但必须含有字母
  11. C++STL与泛型编程__侯捷视频_学习博客_总目录
  12. VB6调用API打开目标文件所在文件夹且选中目标文件
  13. C++中cin的常用用法
  14. Java 设计模式 之 工厂方法模式、抽象工厂模式
  15. 最新HTML5财经直播聊天室喊单直播间系统源码 Redis缓存技术
  16. “野火FreeRTOS教程”第7章补充知识点-异常流程
  17. Linux设备驱动-模块加载过程
  18. 梁宁增长思维30讲笔记 - 模式
  19. 海龟图c语言编程,【编程课堂】海龟作图
  20. ▲什么是类?类有什么作用?

热门文章

  1. Ambrosus宣布推出用于Web Apps、iOS、Android的源代码开发套件
  2. html5--6-68 实战前的准备工作:了解HTML5大纲算法
  3. 信息安全系统设计基础第二次实验
  4. maven 工程启动找不到 Spring ContextLoaderListener 的解决办法
  5. Android非UI线程更新UI的几种方法
  6. 使用元数据设计测试用例
  7. Oracle VM VirtualBox下各种视图切换
  8. 多线程批量ping服务器
  9. 解决media player内部应用程序错误
  10. 前台开发之HTML定义语义化