前言

在日常使用集合时,我们通常使用迭代器来处理集合中的数据,假如有一个用户列表 List,我们想要将用户按照性别分组生成 Map<String, List>。需要遍历 List,然后判断 Map 中是否存在该性别对应的键,如果没有该性别就放入。如果有则拿出值List,添加 User 之后再放入。整个流程看起来没有问题。但我们都知道在数据库中 SQL 语言为我们提供了很多方法可以方便的处理数据如 group by,limit,where 条件等等可以很方便的对数据进行分组,Java 是否可以有这样的实现呢?答案是有的,Java8 Stream流就提供了对应的API。

本篇博客是上篇博客关于 Stream 流介绍的延续。如果读者对 Stream 流,函数式编程不了解。可以先读我的前几篇博客

一文带你深入了解 Lambda 表达式和方法引用

还在用迭代器处理集合吗?试试Stream,真香

声明:本文首发于博客园,作者:后青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 转载请注明,谢谢!

收集器

先看看用收集器实现的分组 List 的方法吧:

List<User> userList = new ArrayList<>();
User user1 = new User(1,"张三","beijing");
User user2 = new User(1,"李四","xi'an");
User user3 = new User(1,"Keats","xi'an");
userList.add(user1);
userList.add(user2);
userList.add(user3); Map<String, List<User>> result = userList.stream().collect(Collectors.groupingBy(User::getLocation));
Set<String> keySet = result.keySet();
keySet.forEach(key -> System.out.println(result.get(key)));

控制台输出

[User{sex=1, name='李四', location='xi'an'}, User{sex=1, name='Keats', location='xi'an'}]
[User{sex=1, name='张三', location='beijing'}]

看了之后,是不是感觉 Stream 真方便!接下来我就向大家介绍一下 Stream 收集器 collect 的用法。

将流元素规约/汇总为一个值

流元素计数

假如需要知道集合中来自西安的用户数量,可以使用 collect(Collectors.counting()) 方法

Long collect = userList.stream().filter(u -> "xi'an".equals(u.getLocation())).collect(Collectors.counting());

也可以简写成:

Long collect = userList.stream().filter(u -> "xi'an".equals(u.getLocation())).count();

最大,最小值

如果需要知道用户列表中最大年龄的用户

Optional<User> maxAge = userList.stream().collect(Collectors.maxBy(Comparator.comparing(User::getAge)));

也可以简写成:

Optional<User> maxAge = userList.stream().max(Comparator.comparing(User::getAge));

相应的最小年龄的用户只需要使用 minBy() 或者 min() 方法就可以了

汇总

如果要知道用户列表中的年龄和

Integer sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));

Integer sumAge = userList.stream().mapToInt(User::getAge).sum();

这里使用了 summingInt / mapToInt 方法,将返回值限定为 Integer 类型,其他包装类型也有对应的 summingXxx 方法。这里不再赘述

拼接字符串

如果要将所有的用户姓名拼接成一个字符串

String nameStr = userList.stream().map(User::getName).collect(Collectors.joining());

Collectors.joining() 该方法会默认采用 StringBuilder 方法将 Stream 拼接成一个字符串返回。

另外该方法还提供了两个重载方法,可以自己设置分隔符 joining(CharSequence delimiter),或者设置分隔符,转换后字符串的前缀及后缀 joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

元素分组

就像收集器开篇的代码一样,使用 Collectors.groupingBy() 方法可以快速的将流中元素分组。有的时候有些分组条件比较复杂,并不能通过方法引用直接分组。比如年龄小于等于 18 岁成为小孩,大于 18 岁成为大人。这时就需要自己写 Lambda 表达式来实现了

Map<String, List<User>> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}));

多级分组

如果想要年龄分组之后,根据位置分组的两重分组,可以这么实现:

Map<String, Map<Integer, List<User>>> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}, Collectors.groupingBy(User::getAge)));

groupingBy() 方法支持重载方法,groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) 这个方法第二个参数是一个新的收集器,你可以创建下级分组,可以计数/求最值/拼串等等。你还可以继续调用 groupingBy 实现更深层次的分组。这里我举一个计数的例子

Map<String, Long> result = userList.stream().collect(Collectors.groupingBy(u -> {
if (u.getAge() <= 18) {
return "小孩";
} else {
return "大人";
}
}, Collectors.counting()));

其他用法大家可以结合博客前面介绍的 API 自行发挥

元素分区

分区可以看成是分组的特殊情况,他所返回的 Map key 是 boolean 类型的。故它的分类函数是一个谓词函数(返回布尔值的函数)。例如我们将年龄大于18的设为true,可以有下面的分区方法:

Map<Boolean, List<User>> result = userList.stream().collect(Collectors.partitioningBy(u -> u.getAge() > 18));

总结

通过这两篇博客对 Stream 流的理解,相信大家和我一样感受到 Stream 流在处理数据时得天独厚的优势。希望大家在读完博客之后回过头看看自己项目中的一些处理集合的代码,是否可以使用流重构一下,在项目中使用它加深记忆。而不是看完就忘。去领略函数式编程的魅力吧!

码字不易,如果你觉得读完以后有收获,不妨点个推荐让更多的人看到吧!

用Stream流轻易的收集数据的更多相关文章

  1. 详解 stream流

    在本人之前的博文中,我们学习了 I/O流.NIO流的相关概念. 那么,在JDK8的更新内容中,提出了一个新的流 -- stream流 那么,现在,本人就来讲解下这个流: 目录 stream流 常用AP ...

  2. Java8新特性 Stream流式思想(三)

    Stream接口中的常用方法 forEach()方法package cn.com.cqucc.demo02.StreamMethods.Test02.StreamMethods; import jav ...

  3. java中的Stream流

    java中的Stream流 说到Stream便容易想到I/O Stream,而实际上,谁规定"流"就一定是"IO流"呢?在Java 8中,得益于Lambda所带 ...

  4. JDK8新特性---stream流

    项目上用到了stream流,找篇blog,转载一下,介绍下Stream流的用法. 1 流概述  流是 JDK8 新增的成员,允许以声明性方式处理数据集合,可以把 Stream 流看作是遍历数据集合的一 ...

  5. 第46天学习打卡(四大函数式接口 Stream流式计算 ForkJoin 异步回调 JMM Volatile)

    小结与扩展 池的最大的大小如何去设置! 了解:IO密集型,CPU密集型:(调优)  //1.CPU密集型 几核就是几个线程 可以保持效率最高 //2.IO密集型判断你的程序中十分耗IO的线程,只要大于 ...

  6. Java 8 (5) Stream 流 - 收集数据

    在前面已经使用过collect终端操作了,主要是用来把Stream中的所有元素结合成一个List,在本章中,你会发现collect是一个归约操作,就像reduce一样可以接受各种做法作为参数,将流中的 ...

  7. java8中用流收集数据

    用流收集数据 汇总 long howManyDishes = menu.stream().collect(Collectors.counting()); int totalCalories = men ...

  8. 《Java 8 in Action》Chapter 6:用流收集数据

    1. 收集器简介 collect() 接收一个类型为 Collector 的参数,这个参数决定了如何把流中的元素聚合到其它数据结构中.Collectors 类包含了大量常用收集器的工厂方法,toLis ...

  9. ASP.NET Core MVC中Controller的Action如何直接使用Response.Body的Stream流输出数据

    在ASP.NET Core MVC中,我们有时候需要在Controller的Action中直接输出数据到Response.Body这个Stream流中,例如如果我们要输出一个很大的文件到客户端浏览器让 ...

随机推荐

  1. util.Date与sql.Date的异同以及相互转换

    Java中有两个Date类 一个是java.util.Date通常情况下用它获取当前时间或构造时间 另一个是java.sql.Date是针对SQL语句使用的,它只包含日期而没有时间部分 两个类型的时间 ...

  2. Jmeter发送jdbc请求进行大批量造数

    创建批量造数脚本,一个简单的结构如下图所示, 1.线程组(10个线程重复运行2次,相当于造20个数) 2.用户定义变量(这是全局变量,用于后面随机筛选用) 3.数据库连接配置 4.计数器(用于主键递增 ...

  3. 详解 File类

    在讲解File类之前,本人先要讲解下 路径,因为我们对于文件的操作是离不开路径的: 目录 路径: File类 文件名称过滤器: 路径: 请观看本人博文 -- <详解 绝对路径与 相对路径> ...

  4. C#开发BIMFACE系列34 服务端API之模型对比5:获取模型构件对比差异

    系列目录     [已更新最新开发文章,点击查看详细] BIMFACE平台提供了服务端“获取修改构件属性差异”API,其返回的结果也是一个列表,仅针对修改的构件(不包含新增.删除的构件),是指对于一个 ...

  5. 【认证与授权】Spring Security的授权流程

    上一篇我们简单的分析了一下认证流程,通过程序的启动加载了各类的配置信息.接下来我们一起来看一下授权流程,争取完成和前面简单的web基于sessin的认证方式一致.由于在授权过程中,我们预先会给用于设置 ...

  6. 深入理解BIO、NIO、AIO

    导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...

  7. hash算法解决冲突的方案

    1, 开放定址法: 所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入 公式为:fi(key) = (f(key)+di) MOD m ...

  8. 2019-2020-1 20199329《Linux内核原理与分析》第七周作业

    <Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...

  9. [Inno Setup] 退出安装程序的两种方式

    1. 完全静默的退出 procedure ExitProcess(exitCode:integer); external 'ExitProcess@kernel32.dll stdcall'; ... ...

  10. vue-cli创建的webpack工程中引用ExtractTextPlugin导致css背景图设置无效的解决方法

    当我们用vue-cli创建项目后,如果在我们的template模板文件中的css样式设置中,有设置了background-image的属性,并且url值传入的是相对路径,那么当我们在打包生产代码时,w ...