Java 8 - Stream流骚操作解读2_归约操作

简介: Java 8 - Stream流骚操作解读2_归约操作

20200510181139786.png


Pre


Java 8 - Stream流骚操作解读见到过的终端操作都是返回一个 boolean ( allMatch 之类的)、 void( forEach )或 Optional 对象( findAny 等)。也见过了使用 collect 来将流中的所有元素组合成一个 List 。


我们这里将学会如何把一个流中的元素组合起来,使用 reduce 操作来表达更复杂的查询,比如“计算菜单中的总卡路里”或“菜单中卡路里最高的是哪一个”。


什么是归约操作


此类查询需要将流中所有元素反复结合起来,得到一个值,比如一个 Integer 。这样的查询可以被为归约操作 (将流规约成一个值)

用函数式编程语言的术语来说,这称为折叠(fold),因为你可以将这个操作看成把一张长长的纸(你的流)反复折叠成一个小方块,而这就是规约操作的结果。


元素求和 reduce

在我们研究如何使用 reduce 方法之前,先来看看如何使用 for-each 循环来对数字列表中的元素求和

int sum = 0;
for (int x : numbers) {
  sum += x;
}


numbers 中的每个元素都用加法运算符反复迭代来得到结果。通过反复使用加法,你把一个数字列表归约成了一个数字。这段代码中有两个参数

  • 总和变量的初始值,在这里是 0
  • 将列表中所有元素结合在一起的操作,在这里是 + 。


要是还能把所有的数字相?,而不必去复制粘贴这段代码,岂不是很好?这正是 reduce 操作的用武之地,它对这种重复应用的模式做了抽象。你可以像下面这样对流中所有的元素求和:

int sum = numbers.stream().reduce(0, (a, b) -> a + b);

reduce 接受两个参数:

  • 一个初始值,这里是0;
  • 一个 BinaryOperator 来将两个元素结合起来产生一个新值,这里我们用的是 lambda (a, b) -> a + b

你也很容易把所有的元素相?,只需要将另一个Lambda: (a, b) -> a * b 传递给 reduce操作就可以了

int product = numbers.stream().reduce(1, (a, b) -> a * b);


我们把demo完善一下

   public static void reduce(){
        List<Integer> list = Arrays.asList(11,2,3);
        int sum = list.stream().reduce(0, (a, b) ->a+b);
        System.out.println(sum);
        int cal = list.stream().reduce(1,(a, b) ->a*b);
        System.out.println(cal);
    }


20210308233220552.png


展示了 reduce 操作是如何作用于一个流的:Lambda反复结合每个元素,直到流被归约层一个新的值。


reduce如何运行的

我们深入研究一下 reduce 操作是如何对一个数字流求和的。



20210308233441582.png

首先, 0 作为Lambda( a )的第一个参数,从流中获得 4 作为第二个参数( b ), 0 + 4 得到 4 ,它成了新的累积值。

然后再用累积值和流中下一个元素 5 调用Lambda,产生新的累积值 9 。

接下来,再用累积值和下一个元素 3调用Lambda,得到 12 。

最后,用 12 和流中最后一个元素 9 调用Lambda,得到最终结果 21



你可以使用方法引用让这段代码更简洁。在Java 8中, Integer 类现在有了一个静态的 sum方法来对两个数求和,这?好是我们想要的,用不着反复用Lambda写同一段代码了:

    public static void reduce(){
        List<Integer> list = Arrays.asList(11,2,3);
        int sum = list.stream().reduce(0,Integer::sum);
        System.out.println(sum);
    }


【无初始值】

reduce 还有一个重载的变体,它不接受初始值,但是会返回一个 Optional 对象:

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));


为什么它返回一个 Optional 呢?考虑流中没有任何元素的情况。 reduce 操作无法返回其和,因为它没有初始值。这就是为什么结果被包?在一个 Optional 对象里,以表明和可能不存在。


最大值和最小值


原来,只要用归约就可以计算最大值和最小值了!让我们来看看如何利用??学到的 reduce来计算流中最大或最小的元素。

reduce 接受两个参数:

  • 一个初始值
  • 一个Lambda来把两个流元素结合起来并产生一个新值

Lambda是一步步用加法运算符应用到流中每个元素上的, 。因此,你需要一个给定两个元素能够返回最大值的Lambda。 reduce 操作会考虑新值和流中下一个元素,并产生一个新的最大值,直到整个流消耗完

可以像下面这样使用 reduce 来计算流中的最大值

Optional<Integer> max = numbers.stream().reduce(Integer::max);


20210308235405159.png



要计算最小值,你需要把 Integer.min 传给 reduce 来替换 Integer.max :

Optional<Integer> min = numbers.stream().reduce(Integer::min);


相关文章
|
4天前
|
分布式计算 DataWorks Java
DataWorks操作报错合集之在使用MaxCompute的Java SDK创建函数时,出现找不到文件资源的情况,是BUG吗
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
17 0
|
4天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
14 0
|
2天前
|
XML 前端开发 Oracle
16:JSP简介、注释与Scriptlet、Page指令元素、Include操作、内置对象、四种属性-Java Web
16:JSP简介、注释与Scriptlet、Page指令元素、Include操作、内置对象、四种属性-Java Web
8 2
|
3天前
|
分布式计算 Java API
Java 8新特性之Lambda表达式与Stream API
【5月更文挑战第1天】本文将介绍Java 8中的两个重要特性:Lambda表达式和Stream API。Lambda表达式是一种新的函数式编程语法,可以简化代码并提高可读性。Stream API是一种用于处理集合的新工具,可以方便地进行数据操作和转换。通过结合Lambda表达式和Stream API,我们可以更加简洁高效地编写Java代码。
|
4天前
|
存储 NoSQL 安全
java 中通过 Lettuce 来操作 Redis
java 中通过 Lettuce 来操作 Redis
java 中通过 Lettuce 来操作 Redis
|
4天前
|
Java
Java8 Stream Collectors groupingBy使用
Java8 Stream Collectors groupingBy使用
|
5天前
|
分布式计算 DataWorks 监控
DataWorks操作报错合集之DataWorks在调用java sdk的createFile功能时报错com.aliyuncs.exceptions.ClientException: 1201111000 如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
9 1
|
5天前
|
存储 前端开发 测试技术
《手把手教你》系列技巧篇(三十五)-java+ selenium自动化测试-单选和多选按钮操作-下篇(详解教程)
【4月更文挑战第27天】本文介绍了使用Java+Selenium进行Web自动化测试时,如何遍历和操作多选按钮的方法。文章分为两个部分,首先是一个本地HTML页面的示例,展示了多选按钮的HTML代码和页面效果,并详细解释了遍历多选按钮的思路:找到所有多选按钮的共同点,通过定位这些元素并放入list容器中,然后使用for循环遍历并操作。 第二部分介绍了在JQueryUI网站上的实战,给出了被测网址,展示了代码设计,同样使用了findElements()方法获取所有多选按钮并存储到list中,然后遍历并进行点击操作。最后,文章对整个过程进行了小结,并推荐了作者的其他自动化测试教程资源。
13 0
|
7天前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(三十三)-java+ selenium自动化测试-单选和多选按钮操作-上篇(详解教程)
【4月更文挑战第25天】本文介绍了自动化测试中如何处理单选和多选按钮的操作,包括它们的定义、HTML代码示例以及如何判断和操作这些元素。文章通过一个简单的HTML页面展示了单选和多选框的示例,并提供了Java+Selenium实现的代码示例,演示了如何检查单选框是否选中以及如何进行全选操作。
13 0
|
7天前
|
Java API 开发者
【专栏】Java 8新特性之Stream API详解
【4月更文挑战第27天】Java 8的Stream API是处理集合数据的新方式,强调简洁和声明式编程。它基于延迟执行和惰性求值,提供创建、中间操作(如filter、map)和终端操作(如forEach、collect)。示例展示了如何通过Stream排序、过滤、映射和聚合数据。