JDK都出到20多了,你还不会使用JDK8的Stream流写代码吗?
目录
前言
Stream流 是什么?
为什么要用Steam流
常见stream流使用案例
映射 map() & 集合 collect()
单字段映射
多字段映射
映射为其他的对象
映射为 Map
去重 distinct()
过滤 filter()
Stream流的其他方法
使用Stream流的弊端
前言
当你某天看到舍友的代码不再写for循环时,你的反应:
你还在 new Collection<>() ,写着for循环的时候,舍友已经开始偷偷卷你,更改代码风格了
本文将带着大家简单理解 Stream 流,并通过部分案例描述 Stream 流 的实用方法
Stream流 是什么?
Stream 流是 Java 8 引入的一个强大工具,它提供了一种全新的方式来处理集合和数组等数据源,使得数据处理变得更加简单、高效和易于理解。
通俗的理解起来就是提供了一种更加便利的遍历处理方式。
如果你要问我 Stream流用起来什么感觉?
那我只能说,,这种感觉就像飞翔在~~
噢不对,,感觉就是:
为什么要用Steam流
Stream 流的主要用途是提供一种高效且表达力高的方式来处理集合和数组等数据源。通过使用 Stream 流,可以避免显式的迭代器和循环,使得代码更加简洁、易读。Stream 流支持复杂的查询/过滤、映射/转换、归约/汇总等操作,能够极大地简化数据处理的复杂度。
总结起来还是:简洁、易读
当然,这也让你的代码看起来更高级那么一点点~~
如下案例,拿到所有的评论的id 集合的两种方法。
- 第一种-for循环便利获取
List<Comment> list = commentMapper.selectList(wrapper);List<Integer> commentId = new ArrayList<>();for(Comment c : list){commentId.add(c.getId());}
- 第二种-Stream流获取
List<Comment> list = commentMapper.selectList(wrapper);List<Integer> commentId = list.stream().map(Comment::getId).collect(Collectors.toList());
两种方法的区别显而易见
下面介绍stream流比较实用的方法
常见stream流使用案例
在这里我们准备一个简单的对象来进行案例测试,只约定两个字段。
@Data
@AllArgsConstructor
public class StreamTestObject {Integer id1;Integer id2;}
映射 map() & 集合 collect()
map() 方法是最常用的方法之一,它可以将流中的每个元素转换成另一种形式,返回转换后的 Stream。
如前文的例子所示,
单字段映射
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));// 便于观察变化Stream<StreamTestObject> stream = streamTestObjects.stream();Stream<Integer> id1Stream = streamTestObjects.stream().map(StreamTestObject::getId1);
看代码我们可以看到,map方法将对象的stream流映射为了其中 id1 这个字段的stream流
拿到这个字段的流后,可以做些什么呢?
最常用的方法之一就是与 集合 collect() 搭配起来使用。
那么 collect() 方法能做写什么呢?
- 用途:将流中的元素累积成一个汇总结果,我们可以按照自己的需求将结果汇总为一个 List、Set、Map 等
如下代码所示
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));Stream<StreamTestObject> stream = streamTestObjects.stream();Stream<Integer> id1Stream = streamTestObjects.stream().map(StreamTestObject::getId1);List<Integer> collectList = id1Stream.collect(Collectors.toList());
// Set<Integer> collectSet = id1Stream.collect(Collectors.toSet());// 连起来使用一行代码可以写成这样collectList = streamTestObjects.stream().map(StreamTestObject::getId1).collect(Collectors.toList());
// collectSet = streamTestObjects.stream().map(StreamTestObject::getId1).collect(Collectors.toSet());System.out.println("collectList:" + collectList);
// 输出结果 collectList:[1, 3, 5]
结果能够把 id1 成功收集起来,代码的易读性也体现在其中。我们一眼就能看出这行代码映射了id1 这个字段为一个 List 或 Set。
多字段映射
那如果我们想要对象集合中的 id1 和 id2 都汇总到一个 List<Integer> 集合里,应该如何操作呢。
这里我们可以使用一个 flatMap() 方法
- 用途:将流中的每个元素都转换成另一个流,然后将所有流连接成一个流。
直接上代码
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(5, 6));List<Integer> collectList = streamTestObjects.stream().flatMap(object ->Stream.of(object.getId1(), object.getId2())).collect(Collectors.toList());System.out.println("collectList:" + collectList);//输出结果 collectList:[1, 2, 3, 4, 5, 6]
在这个例子中,Stream.of(obj.getId1(), obj.getId2())为每个对象生成了一个包含两个ID的流,它在map中 形成了一个临时的流
然后flatMap将这些流“展平”成了一个包含所有ID的流,最后我们通过collect(Collectors.toList())将这个流收集到了一个列表中。
映射为其他的对象
有的时候的业务需求需要我们把一个对象集合转化为另外一个集合对象,如果是单纯的 字段copy,我们可以使用 BeanUtils 或者 MapStruct 等方法实现。
如果转化的过程中设计业务逻辑,那么就需要 Stream流出手了。
这里需要设计到一个显示 return 的写法,上代码,先准备一个另外的对象,只包含一个id字段
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StreamOtherObject {Integer id;}
然后 我们将上述测试对象的id1,转化为这里的id字段。
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamOtherObject> collectList = streamTestObjects.stream().map(streamTestObject -> {StreamOtherObject object = new StreamOtherObject();object.setId(streamTestObject.getId1());return object;}).collect(Collectors.toList());System.out.println("collectList:" + collectList);//输出结果 collectList:[StreamOtherObject(id=1), StreamOtherObject(id=3), StreamOtherObject(id=4)]
注意看返回的集合对象已经是我在表达式里return 的 StreamOtherObject了。
也就是 return 的内容就是集合的具体对象
映射为 Map
Stream流还能把集合映射为一个Map,这里我们测试用例为将映射结果设置为 key 为 id1, value 为对象本身
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4));Map<Integer, StreamTestObject> map = streamTestObjects.stream().collect(Collectors.toMap(StreamTestObject::getId1,Function.identity(),(existsOne, replaceOne) -> replaceOne));System.out.println("collectMap:" + map);//输出结果 collectMap:{1=StreamTestObject(id1=1, id2=2), 3=StreamTestObject(id1=3, id2=4)}
可以看到,toMap() 方法中,传递了3个参数,前两个分别为 key , value
第三个参数传了一个表达式,这里的逻辑表示如果发生冲突,就保留 Map中新的那个对象,而不是保留它。同时,第三个参数也处理了冲突,如果你没有对于 key 相同的情况做处理,也就是 key 冲突了,方法将抛出一个IllegalStateException。
所以你需要做对应的处理,如try catch 下来,或者进行冲突处理,即传递第三个参数。
去重 distinct()
对于拿到的流结果,我们有的时候有去重的需求,当然我们可以转为 toSet() 进行去重
stream流同样提供了一个方法进行去重,就是 distinct() 方法
- 用途:去除流中的重复元素,返回包含不同元素的 Stream。
这里比较好理解,我们直接看案例
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<Integer> collectList = streamTestObjects.stream().flatMap(object ->Stream.of(object.getId1(), object.getId2())).distinct().collect(Collectors.toList());System.out.println("collectList:" + collectList);//输出结果 collectList:[1, 2, 3, 4, 5]
过滤 filter()
- 用途:根据提供的条件过滤元素,返回满足条件的 Stream。
过滤的方法也是比较常用的方法,也是比较多业务中有这个需求的。这里介绍两种方法
在拿到一个流后,也许不是所有的元素我们都需要。我们需要保存满足特定条件的元素,这时候就可以使用 filter方法来实现。
这里的案例表示筛选除 id1 为 1,id2 为 4 的数据,代码如下:
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamTestObject> collectList = streamTestObjects.stream().filter(object -> object.getId1().equals(1) || object.getId2().equals(4)).collect(Collectors.toList());System.out.println("collectList:" + collectList);//输出结果 collectList:[StreamTestObject(id1=1, id2=2), StreamTestObject(id1=3, id2=4)]
如果你的过滤逻辑比较复杂可以使用显示 return 写法来过滤
List<StreamTestObject> streamTestObjects = Arrays.asList(new StreamTestObject(1, 2),new StreamTestObject(3, 4),new StreamTestObject(4, 5));List<StreamTestObject> collectList = streamTestObjects.stream().filter(object -> {int id1 = 1;int id2 = 4;return object.getId1().equals(id1) || object.getId2().equals(id2);}).collect(Collectors.toList());System.out.println("collectList:" + collectList);//输出结果 collectList:[StreamTestObject(id1=1, id2=2), StreamTestObject(id1=3, id2=4)]
在代码块里可以编辑自己自定义的过滤逻辑
这里要注意返回值是一个布尔值,如果为 true,则保留这项数据,不满足,则进行一项数据处理。
Stream流的其他方法
前文是 Stream 流比较常见的方法案例,它还提供了很多其他的接口来实现对应的场景,如:
- sorted()
- 用途:对流中的元素进行自然排序(需实现 Comparable 接口),返回排序后的 Stream。
- 示例:对用户列表按年龄进行排序。
- limit(long maxSize)
- 用途:截断流,使其包含不超过给定数量的元素,返回截断后的 Stream。
- 示例:只取用户列表中的前三个用户。
- skip(long n)
- 用途:跳过流中的前 n 个元素,返回剩下的元素的 Stream。
- 示例:跳过用户列表中的前两个用户,取后面的用户。
- forEach(Consumer<? super T> action)
- 用途:这是大家比较熟悉的操作,在代码编写中可以省去 .Stream() 的写法。意为对流中的每个元素执行提供的操作,这是一个终结操作。
- 示例:遍历用户列表并打印每个用户的名字。
使用Stream流的弊端
学习了Stream流 的优点之后,也需要知道随之产生的弊端有短些,这里我列举几个主要的内容
- 性能问题:
- 多次遍历:有时为了完成一个操作,可能需要多次遍历数据源。例如,先过滤(filter)再映射(map)最后收集(collect),这会导致数据被多次遍历。
- 并行流开销:虽然并行流可以加速处理过程,但它们引入了额外的线程管理开销,并且不总是能带来性能提升,尤其是在数据源较小或操作相对简单时。
- 懒加载导致的意外行为:Stream操作是懒加载的,这意味着它们直到需要结果时才会执行。这可能导致在调试时难以追踪问题的源头,或者在某些情况下,当流操作依赖于外部状态时,可能导致不可预测的行为。
- 可读性和维护性:
??:前面不是可读性强吗,怎么有问题了?如果嵌套太多层的操作方法,也会使得表达式的可读性降低
- 复杂逻辑难以追踪:对于包含多个复杂操作(如多重过滤、映射、归约等)的Stream链,其逻辑可能变得难以理解和追踪。
- 调试困难:由于Stream操作的延迟执行和中间操作的无状态性,调试Stream代码可能会比传统循环更加困难。
- 错误处理:
- 异常处理复杂:在Stream操作中处理异常(如尝试映射一个可能抛出异常的函数)比在传统循环中更复杂。Stream API没有直接支持异常处理机制,通常需要通过try-catch块或自定义函数来处理。
- 内存消耗:
- 中间结果存储:Stream API在执行过程中可能会创建中间结果的临时集合,尤其是在进行复杂操作时,这可能会增加内存消耗。
到这里,同学们可以多实操一下这些方法来巩固知识。文章如有遗漏或建议更改的部分欢迎佬们指出。
相关文章:

JDK都出到20多了,你还不会使用JDK8的Stream流写代码吗?
目录 前言 Stream流 是什么? 为什么要用Steam流 常见stream流使用案例 映射 map() & 集合 collect() 单字段映射 多字段映射 映射为其他的对象 映射为 Map 去重 distinct() 过滤 filter() Stream流的其他方法 使用Stream流的弊端 前言 当你某天看…...

QT slots 函数
文章目录 概述小结 概述 在Qt中,slots 是一种特殊的成员函数,它们可以与对象发出的信号连接。当信号被触发时,连接的槽函数会被调用。 来个简单的示例吧,如下图: #include <QObject> #include <QDebug>…...

pycharm如何使用jupyter
目录 配置jupyter新建jupyter文件别人写的方法(在pycharm种安装,在网页中使用) pycharm专业版 配置jupyter 在pycharm终端启动一个conda虚拟环境,输入 conda install jupyter会有很多前置包需要安装: 新建jupyter…...

机器学习——无监督学习(k-means算法)
1、K-Means聚类算法 K表示超参数个数,如分成几个类别,K值就取多少。若无需求,可使用网格搜索找到最佳的K。 步骤: 1、随机设置K个特征空间内的点作为初始聚类中心; 2、对于其他每个点计算到K个中心的距离,…...

强化学习-6 DDPG、PPO、SAC算法
文章目录 1 DPG方法2 DDPG算法3 DDPG算法的优缺点4 TD3算法4.1 双Q网络4.2 延迟更新4.3 噪声正则 5 附15.1 Ornstein-Uhlenbeck (OU) 噪声5.1.1 定义5.1.2 特性5.1.3 直观理解5.1.4 数学性质5.1.5 代码示例5.1.6 总结 6 重要性采样7 PPO算法8 附28.1 重要性采样方差计算8.1.1 公…...

vue3实现多表头列表el-table,拖拽,鼠标滑轮滚动条优化
需求背景解决效果index.vue 需求背景 需要实现多表头列表的用户体验优化 解决效果 index.vue <!--/** * author: liuk * date: 2024-07-03 * describe:**** 多表头列表 */--> <template><el-table ref"tableRef" height"calc(100% - 80px)&qu…...

Micron近期发布了32Gb DDR5 DRAM
Micron Technology近期发布了一项内存技术的重大突破——一款32Gb DDR5 DRAM芯片,这项创新不仅将存储容量翻倍,还显著提升了针对人工智能(AI)、机器学习(ML)、高性能计算(HPC)以及数…...

SQL Server时间转换
第一种:format --转化成年月日 select format( GETDATE(),yyyy-MM-dd) --转化年月日,时分秒,这里的HH指24小时的,hh是12小时的 select format( GETDATE(),yyyy-MM-dd HH:mm:ss) --转化成时分秒的,这里就不一样的&…...

kubernetes集群部署:node节点部署和CRI-O运行时安装(三)
关于CRI-O Kubernetes最初使用Docker作为默认的容器运行时。然而,随着Kubernetes的发展和OCI标准的确立,社区开始寻找更专门化的解决方案,以减少复杂性和提高性能。CRI-O的主要目标是提供一个轻量级的容器运行时,它可以直接运行O…...

03:Spring MVC
文章目录 一:Spring MVC简介1:说说自己对于Spring MVC的了解?1.1:流程说明: 一:Spring MVC简介 Spring MVC就是一个MVC框架,Spring MVC annotation式的开发比Struts2方便,可以直接代…...

玩转springboot之springboot注册servlet
springboot注册servlet 有时候在springboot中依然需要注册servlet,filter,listener,就以servlet为例来进行说明,另外两个也都类似 使用WebServlet注解 在servlet3.0之后,servlet注册支持注解注册,而不需要在…...

推荐好玩的工具之OhMyPosh使用
解除禁止脚本 Set-ExecutionPolicy RemoteSigned 下载Oh My Posh winget install oh-my-posh 或者 Install-Module oh-my-posh -Scope AllUsers 下载Git提示 Install-Module posh-git -Scope CurrentUser 或者 Install-Module posh-git -Scope AllUser 下载命令提示 Install-Mo…...

pydub、ffmpeg 音频文件声道选择转换、采样率更改
快速查看音频通道数和每个通道能力判断具体哪个通道说话;一般能量大的那个算是说话 import wave from pydub import AudioSegment import numpy as npdef read_wav_file(file_path):with wave.open(file_path, rb) as wav_file:params wav_file.getparams()num_cha…...

0803实操-Windows Server系统管理
Windows Server系统管理 系统管理与基础配置 查看系统信息、更改计算机名称 网络配置 启用网络发现 Windows启用网络发现是指在网络设置中启用一个功能,该功能允许您的计算机在网络上识别和访问其他设备和计算机。具体来说,启用网络发现后ÿ…...

使用Java构建物联网应用的最佳实践
使用Java构建物联网应用的最佳实践 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 随着物联网(IoT)技术的快速发展,越来越…...

价格预言机的使用总结(一):Chainlink篇
文章首发于公众号:Keegan小钢 前言 价格预言机已经成为了 DeFi 中不可获取的基础设施,很多 DeFi 应用都需要从价格预言机来获取稳定可信的价格数据,包括借贷协议 Compound、AAVE、Liquity ,也包括衍生品交易所 dYdX、PERP 等等。…...

【Pyhton】读取寄存器数据到MySQL数据库
目录 步骤 modsim32软件配置 Navicat for MySQL 代码实现 步骤 安装必要的库:确保安装了pymodbus和pymysql。 配置Modbus连接:设置Modbus从站的IP地址、端口(对于TCP)或串行通信参数(对于RTU)。 连接M…...

jmeter-beanshell学习3-beanshell获取请求报文和响应报文
前后两个报文,后面报文要用前面报文的响应结果,这个简单,正则表达式或者json提取器,都能实现。但是如果后面报文要用前面请求报文的内容,感觉有点难。最早时候把随机数写在自定义变量,前后两个接口都用这个…...

【C++】B树及其实现
写目录 一、B树的基本概念1.引入2.B树的概念 二、B树的实现1.B树的定义2.B树的查找3.B树的插入操作4.B树的删除5.B树的遍历6.B树的高度7.整体代码 三、B树和B*树1.B树2.B*树3.总结 一、B树的基本概念 1.引入 我们已经学习过二叉排序树、AVL树和红黑树三种树形查找结构&#x…...

C++(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例
C(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例 文章目录 C(Qt)-GIS开发-QGraphicsView显示瓦片地图简单示例1、概述2、实现效果3、主要代码4、源码地址 更多精彩内容👉个人内容分类汇总 👈👉GIS开发 👈 1、概述 支持多线程加…...

CTFShow的RE题(三)
数学不及格 strtol 函数 long strtol(char str, char **endptr, int base); 将字符串转换为长整型 就是解这个方程组了 主要就是 v4, v9的关系, 3v9-(v10v11v12)62d10d4673 v4 v12 v11 v10 0x13A31412F8C 得到 3*v9v419D024E75FF(1773860189695) 重点&…...

WordPress主题开发进群付费主题v1.1.2 多种引流方式
全新前端UI界面,多种前端交互特效让页面不再单调,进群页面群成员数,群成员头像名称,每次刷新页面随机更新不重复,最下面评论和点赞也是如此随机刷新不重复 进群页面简介,群聊名称,群内展示&…...

SAP中的 UPDATA TASK 和 BACKGROUND TASK
前言: 记录这篇文章起因是调查生产订单报工问题引申出来的一个问题,后来再次调查后了解了其中缘由,大概记录以下,如有不对,欢迎指正。问题原贴如下: SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更…...

UDP协议:独特之处及其在网络通信中的应用
在网络通信领域,UDP(用户数据报协议,User Datagram Protocol)是一种广泛使用的传输层协议。与TCP(传输控制协议,Transmission Control Protocol)相比,UDP具有其独特的特点和适用场景…...

支持向量机(Support Vector Machine,SVM)及Python和MATLAB实现
支持向量机(Support Vector Machine,SVM)是一种经典的机器学习算法,广泛应用于模式识别、数据分类和回归分析等领域。SVM的背景可以追溯到1990s年代,由Vladimir Vapnik等人提出,并在之后不断发展和完善。 …...

【RT-thread studio 下使用STM32F103-学习sem-信号量-初步使用-线程之间控制-基础样例】
【RT-thread studio 下使用STM32F103-学习sem-信号量-初步使用-线程之间控制-基础样例】 1、前言2、环境3、事项了解(1)了解sem概念-了解官网消息(2)根据自己理解,设计几个使用方式(3)不建议运行…...

使用nodejs输出著作权申请所需的word版源码
使用nodejs输出著作权申请所需的word版源码 背景 软件著作权申请需要提供一份80页的word版源代码,如果手工复制源码到word文档中,工作量将无聊到让任何一个DAO人员血压爆表,因此我们不得不编写一个简单的文本处理代码,通过自动方…...

[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解
[Vite]vite-plugin-react和vite-plugin-react-swc插件原理了解 共同的作用 JSX 支持:插件为 React 应用程序中的 JSX 语法提供支持,确保它可以被正确地转换为 JavaScript。Fast Refresh:提供热更新功能,当应用程序在开发服务器上…...

记一次使用“try-with-resources“的语法导致的BUG
背景描述 最近使用try-catch的时候遇到了一个问题,背景是这样的:当第一次与数据库建立连接以后执行查询完毕并没有手动关闭连接,但是当我第二次获取连接的时候报错了,显示数据库连接失败,连接已经关闭。 org.postgres…...

用Excel处理数据图像,出现交叉怎么办?
一、问题描述 用excel制作X-Y散点图,意外的出现了4个交叉点,而实际上的图表数据是没有交叉的。 二、模拟图表 模拟部分数据,并创建X-Y散点图,数据区域,X轴数据是依次增加的,因此散点图应该是没有交叉的。…...