当前位置: 首页 > news >正文

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 流比较常见的方法案例,它还提供了很多其他的接口来实现对应的场景,如:

  1. sorted()
    • 用途:对流中的元素进行自然排序(需实现 Comparable 接口),返回排序后的 Stream。
    • 示例:对用户列表按年龄进行排序。
  2. limit(long maxSize)
    • 用途:截断流,使其包含不超过给定数量的元素,返回截断后的 Stream。
    • 示例:只取用户列表中的前三个用户。
  3. skip(long n)
    • 用途:跳过流中的前 n 个元素,返回剩下的元素的 Stream。
    • 示例:跳过用户列表中的前两个用户,取后面的用户。
  4. forEach(Consumer<? super T> action)
    • 用途:这是大家比较熟悉的操作,在代码编写中可以省去 .Stream() 的写法。意为对流中的每个元素执行提供的操作,这是一个终结操作。
    • 示例:遍历用户列表并打印每个用户的名字。

使用Stream流的弊端

学习了Stream流 的优点之后,也需要知道随之产生的弊端有短些,这里我列举几个主要的内容

  • 性能问题
  1. 多次遍历:有时为了完成一个操作,可能需要多次遍历数据源。例如,先过滤(filter)再映射(map)最后收集(collect),这会导致数据被多次遍历。
  2. 并行流开销:虽然并行流可以加速处理过程,但它们引入了额外的线程管理开销,并且不总是能带来性能提升,尤其是在数据源较小或操作相对简单时。
  3. 懒加载导致的意外行为:Stream操作是懒加载的,这意味着它们直到需要结果时才会执行。这可能导致在调试时难以追踪问题的源头,或者在某些情况下,当流操作依赖于外部状态时,可能导致不可预测的行为。

  • 可读性和维护性

??:前面不是可读性强吗,怎么有问题了?如果嵌套太多层的操作方法,也会使得表达式的可读性降低

  1. 复杂逻辑难以追踪:对于包含多个复杂操作(如多重过滤、映射、归约等)的Stream链,其逻辑可能变得难以理解和追踪。
  2. 调试困难:由于Stream操作的延迟执行和中间操作的无状态性,调试Stream代码可能会比传统循环更加困难。

  • 错误处理
  1. 异常处理复杂:在Stream操作中处理异常(如尝试映射一个可能抛出异常的函数)比在传统循环中更复杂。Stream API没有直接支持异常处理机制,通常需要通过try-catch块或自定义函数来处理。

  • 内存消耗
  1. 中间结果存储:Stream API在执行过程中可能会创建中间结果的临时集合,尤其是在进行复杂操作时,这可能会增加内存消耗。

到这里,同学们可以多实操一下这些方法来巩固知识。文章如有遗漏或建议更改的部分欢迎佬们指出。

相关文章:

JDK都出到20多了,你还不会使用JDK8的Stream流写代码吗?

目录 前言 Stream流 是什么&#xff1f; 为什么要用Steam流 常见stream流使用案例 映射 map() & 集合 collect() 单字段映射 多字段映射 映射为其他的对象 映射为 Map 去重 distinct() 过滤 filter() Stream流的其他方法 使用Stream流的弊端 前言 当你某天看…...

QT slots 函数

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

pycharm如何使用jupyter

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

机器学习——无监督学习(k-means算法)

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

强化学习-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芯片&#xff0c;这项创新不仅将存储容量翻倍&#xff0c;还显著提升了针对人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&#xff09;、高性能计算&#xff08;HPC&#xff09;以及数…...

SQL Server时间转换

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

kubernetes集群部署:node节点部署和CRI-O运行时安装(三)

关于CRI-O Kubernetes最初使用Docker作为默认的容器运行时。然而&#xff0c;随着Kubernetes的发展和OCI标准的确立&#xff0c;社区开始寻找更专门化的解决方案&#xff0c;以减少复杂性和提高性能。CRI-O的主要目标是提供一个轻量级的容器运行时&#xff0c;它可以直接运行O…...

03:Spring MVC

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

玩转springboot之springboot注册servlet

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

推荐好玩的工具之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 音频文件声道选择转换、采样率更改

快速查看音频通道数和每个通道能力判断具体哪个通道说话&#xff1b;一般能量大的那个算是说话 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启用网络发现是指在网络设置中启用一个功能&#xff0c;该功能允许您的计算机在网络上识别和访问其他设备和计算机。具体来说&#xff0c;启用网络发现后&#xff…...

使用Java构建物联网应用的最佳实践

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

价格预言机的使用总结(一):Chainlink篇

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

【Pyhton】读取寄存器数据到MySQL数据库

目录 步骤 modsim32软件配置 Navicat for MySQL 代码实现 步骤 安装必要的库&#xff1a;确保安装了pymodbus和pymysql。 配置Modbus连接&#xff1a;设置Modbus从站的IP地址、端口&#xff08;对于TCP&#xff09;或串行通信参数&#xff08;对于RTU&#xff09;。 连接M…...

jmeter-beanshell学习3-beanshell获取请求报文和响应报文

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

【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、源码地址 更多精彩内容&#x1f449;个人内容分类汇总 &#x1f448;&#x1f449;GIS开发 &#x1f448; 1、概述 支持多线程加…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...