下面我们一起来了解一下关于Java8中聚合操作collect、reduce方法,希望这篇文章能够帮助到各位java初学者。

Stream的基本概念

Stream和集合的区别:

Stream不会自己存储元素。元素储存在底层集合或者根据需要产生。

Stream操作符不会改变源对象。相反,它会返回一个持有结果的新的Stream。

3.Stream操作可能是延迟执行的,这意味着它们会等到需要结果的时候才执行。

Stream操作的基本过程,可以归结为3个部分:

创建一个Stream。

在一个或者多个操作中,将指定的Stream转换为另一个Stream的中间操作。

通过终止(terminal)方法来产生一个结果。该操作会强制它之前的延时操作立即执行,这之后该Stream就不能再被使用了。

中间操作都是filter()、distinct()、sorted()、map()、flatMap()等,其一般是对数据集的整理(过滤、排序、匹配、抽取等)。

终止方法往往是完成对数据集中数据的处理,如forEach(),还有allMatch()、anyMatch()、findAny()、 findFirst(),数值计算类的方法有sum、max、min、average等等。终止方法也可以是对集合的处理,如reduce()、 collect()等等。reduce()方法的处理方式一般是每次都产生新的数据集,而collect()方法是在原数据集的基础上进行更新,过程中不产生新的数据集。

Listnums = Arrays.asList(1, 3, null, 8, 7, 8, 13, 10);

nums.stream().filter(num -> num != null).distinct().forEach(System.out::println);

上面代码实现为过滤null值并去重,遍历结果,实现简洁明了。使用传统方法就相对繁琐的多。另外其中 forEach即为终止操作方法,如果无该方法上面代码就没有任何操作。filter、map、forEach、findAny等方法的使用都比较简单,这里省略。

下面介绍强大的聚合操作,其主要分为两种:

可变聚合:把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder;

其他聚合:除去可变聚合,剩下的,一般都不是通过反复修改某个可变对象,而是通过把前一次的汇聚结果当成下一次的入参,反复如此。比如reduce,count,allMatch;

聚合操作reduce

Stream.reduce,返回单个的结果值,并且reduce操作每处理一个元素总是创建一个新值。常用的方法有average, sum, min, max, count,使用reduce方法都可实现。这里主要介绍reduce方法:

T reduce(T identity, BinaryOperatoraccumulator)

identity:它允许用户提供一个循环计算的初始值。accumulator:计算的累加器,其方法签名为apply(T t,U u),在该reduce方法中第一个参数t为上次函数计算的返回值,第二个参数u为Stream中的元素,这个函数把这两个值计算apply,得到的和会被赋值给下次执行这个方法的第一个参数。有点绕看代码:

int value = Stream.of(1, 2, 3, 4).reduce(100, (sum, item) -> sum item);

Assert.assertSame(value, 110);

/* 或者使用方法引用 */

value = Stream.of(1, 2, 3, 4).reduce(100, Integer::sum);

这个例子中100即为计算初始值,每次相加计算值都会传递到下一次计算的第一个参数。

reduce还有其它两个重载方法:

Optionalreduce(BinaryOperatoraccumulator):与上面定义基本一样,无计算初始值,所以他返回的是一个Optional。

U reduce(U identity, BiFunction accumulator, BinaryOperator combiner):与前面两个参数的reduce方法几乎一致,你只要注意到BinaryOperator其实实现了BiFunction和BinaryOperator两个接口。

收集结果collect

当你处理完流时,通常只是想查看一下结果,而不是将他们聚合为一个值。先看collect的基础方法,它接受三个参数:

R collect(Suppliersupplier, BiConsumeraccumulator, BiConsumercombiner)

supplier:一个能创造目标类型实例的方法。accumulator:一个将当元素添加到目标中的方法。combiner:一个将中间状态的多个结果整合到一起的方法(并发的时候会用到)。接着看代码:

Streamstream = Stream.of(1, 2, 3, 4).filter(p -> p > 2);

Listresult = stream.collect(() -> new ArrayList<>(), (list, item) -> list.add(item), (one, two) -> one.addAll(two));

/* 或者使用方法引用 */

result = stream.collect(ArrayList::new, List::add, List::addAll);

这个例子即为过滤大于2的元素,将剩余结果收集到一个新的list中。

第一个方法生成一个新的ArrayList;

第二个方法中第一个参数是前面生成的ArrayList对象,第二个参数是stream中包含的元素,方法体就是把stream中的元素加入ArrayList对象中。第二个方法被反复调用直到原stream的元素被消费完毕;

第三个方法也是接受两个参数,这两个都是ArrayList类型的,方法体就是把第二个ArrayList全部加入到第一个中;

代码有点繁琐,或者使用collect的另一个重载方法:

R collect(Collector super="" r=""> collector)

注意到Collector其实是上面supplier、accumulator、combiner的聚合体。那么上面代码就变成:?>

Listlist = Stream.of(1, 2, 3, 4).filter(p -> p > 2).collect(Collectors.toList());

将结果收集到map中

先定义如下Person对象

class Person{

public String name;

public int age;

Person(String name, int age){

this.name = name;

this.age = age;

}

@Override

public String toString(){

return String.format("Person{name='%s', age=%d}", name, age);

}

}

假设你有一个Stream对象,希望将其中元素收集到一个map中,这样就可以根据他的名称来查找对应年龄,例如:

Mapresult = people.collect(HashMap::new,(map,p)->map.put(p.name,p.age),Map::putAll);

/*使用Collectors.toMap形式*/

Mapresult = people.collect(Collectors.toMap(p -> p.name, p -> p.age, (exsit, newv) -> newv));

其中Collectors.toMap方法的第三个参数为键值重复处理策略,如果不传入第三个参数,当有相同的键时,会抛出一个IlleageStateException。

或者你想将Person分解为Map存储:

List> personToMap = people.collect(ArrayList::new, (list, p) -> {

Mapmap = new HashMap<>();

map.put("name", p.name);

map.put("age", p.age);

list.add(map);

}, List::addAll);

分组和分片

对具有相同特性的值进行分组是一个很常见的任务,Collectors提供了一个groupingBy方法,方法签名为:

Collector> groupingBy(Function super="" extends="" k=""> classifier, Collector super="" d=""> downstream)

classifier:一个获取Stream元素中主键方法。downstream:一个操作对应分组后的结果的方法。?>?>

假如要根据年龄来分组:

Map> peropleByAge = people.filter(p -> p.age > 12).collect(Collectors.groupingBy(p -> p.age, Collectors.toList()));

假如我想要根据年龄分组,年龄对应的键值List存储的为Person的姓名,怎么做呢:

Map> peropleByAge = people.collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p.name, Collectors.toList())));

mapping即为对各组进行投影操作,和Stream的map方法基本一致。

假如要根据姓名分组,获取每个姓名下人的年龄总和(好像需求有些坑爹):

MapsumAgeByName = people.collect(Collectors.groupingBy(p -> p.name, Collectors.reducing(0, (Person p) -> p.age, Integer::sum)));

/* 或者使用summingInt方法 */

sumAgeByName = people.collect(Collectors.groupingBy(p -> p.name, Collectors.summingInt((Person p) -> p.age)));

可以看到Java8的分组功能相当强大,当然你还可以完成更复杂的功能。另外Collectors中还存在一个类似groupingBy的方法:partitioningBy,它们的区别是partitioningBy为键值为Boolean类型的groupingBy,这种情况下它比groupingBy更有效率。

join和统计功能

话说Java8中新增了一个StringJoiner,Collectors的join功能和它基本一样。用于将流中字符串拼接并收集起来,使用很简单:

String names = people.map(p->p.name).collect(Collectors.joining(","))

Collectors分别提供了求平均值averaging、总数couting、最小值minBy、最大值maxBy、求和suming等操作。但是假如你希望将流中结果聚合为一个总和、平均值、最大值、最小值,那么Collectors.summarizing(Int/Long/Double)就是为你准备的,它可以一次行获取前面的所有结果,其返回值为(Int/Long/Double)SummaryStatistics。

DoubleSummaryStatistics dss = people.collect(Collectors.summarizingDouble((Person p)->p.age));

double average=dss.getAverage();

double max=dss.getMax();

double min=dss.getMin();

double sum=dss.getSum();

double count=dss.getCount();

java8 reduce的用法_Java8中聚合操作collect、reduce方法详解相关推荐

  1. python中search用法_Python中的python re.search方法详解

    re.search扫描整个字符串并返回第一个成功的匹配,若string中包含pattern子串,则返回Match对象,否则返回None,注意,如果string中存在多个pattern子串,只返回第一个 ...

  2. python怎么横着输出_对python3中, print横向输出的方法详解

    对python3中, print横向输出的方法详解 Python 2 : print打印的时候,如果结尾有逗号,打出来时候不会换行.但是在python3里面就不行了. Python3: 3.0的pri ...

  3. php中读取大文件实现方法详解

    php中读取大文件实现方法详解 来源:   时间:2013-09-05 19:27:01   阅读数:6186 分享到:0 [导读] 本文章来给各位同学介绍php中读取大文件实现方法详解吧,有需要了解 ...

  4. 使用Java操作文本文件的方法详解

    使用Java操作文本文件的方法详解 摘要: 最初java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类 最初java是不支持对文本文件的处理的,为了弥补这个缺憾而 ...

  5. python更新数据库表的时间字段_python更新数据库中某个字段的数据(方法详解)

    连接数据库基本操作,我把每一步的操作是为什么给大家注释一下,老手自行快进. 请注意这是连接数据库操作,还不是更新. import pymysql #导包 #连接数据库 db = pymysql.con ...

  6. linux ipset 流量,linux中ipset命令的使用方法详解

    linux中ipset命令的使用方法详解 发布时间:2020-10-25 17:07:19 来源:脚本之家 阅读:97 作者:lijiaocn 栏目:服务器 ipset介绍 iptables是在lin ...

  7. vue ajax highcharts,在vue项目中引入highcharts图表的方法(详解)

    npm进行highchars的导入,导入完成后就可以进行highchars的可视化组件开发了 npm install highcharts --save 1.components目录下新建一个char ...

  8. Linux中history历史命令使用方法详解

    在/etc/profile里添加如下:#History export HISTTIMEFORMAT="[%F %T]" HISTDIR=/home/common/.hist if ...

  9. python支持向量机回归_Python中支持向量机SVM的使用方法详解

    除了在Matlab中使用PRTools工具箱中的svm算法,Python中一样可以使用支持向量机做分类.因为Python中的sklearn库也集成了SVM算法,本文的运行环境是Pycharm. 一.导 ...

最新文章

  1. 未来属于人工智能工程师,但成功转型不容易
  2. Flowable 数据库表结构 ACT_RU_EVENT_SUBSCR
  3. oracle存储返回sql查询,如何做才能使record类型和table类型存储查询语句返回的多条记录?...
  4. 多台tomcat服务的session共享 memcached与redis
  5. 响应式精美列商城发卡源码
  6. json for java
  7. 机器学习中的两个概率模型
  8. SAP的十年豪赌:不成HANA 便成仁
  9. LOG. Supervisor基本使用
  10. Android保活实现方案梳理
  11. 内存数据库将数据保存在计算机内存,内存数据库
  12. 外贸企业邮箱格式怎么写?外贸域名邮箱格式
  13. SpringDataJPA学习笔记
  14. BZOJ 3039 玉蟾宫
  15. java中boot是什么,Spring boot是什么
  16. 2的30次方用计算机怎么按,计算机基础试题2
  17. 打印机扫描计算机远程扫描仪,未在设备上将扫描类型设置为(远程扫描仪)的解决方法...
  18. OpenCV-4.3.0 Windows版本下载
  19. SecureFx设置密钥登陆
  20. 基于单片机的居家安全报警系统

热门文章

  1. springboot接入华为微服务引擎CSE全过程及后续遇到的问题一览
  2. 室内导航电子地图制作,室内地图可视化平台
  3. EduCoder_web实训作业--JavaScript学习手册五:JS数组
  4. 如何在WampServer和phpStudy中MySQL导入sql文件
  5. 怎么搭建xss平台云服务器,零成本搭建个人专属XSS平台
  6. linux下分析hprof大文件,hprof 分析工具
  7. foxmail登录outlook账号
  8. 什么是css选择器?css3中5种常见的基本选择器-web前端教程
  9. Fuzzing101 Exercise 5 - LibXML2 学习笔记
  10. 《跨界杂谈》企业商业模式(四):弹性