首页 > 其他分享 >Stream流的中间操作和终端操作

Stream流的中间操作和终端操作

时间:2023-07-24 12:12:14浏览次数:33  
标签:map Stream stream Movie list 终端 操作 forEach

最近在写代码时发现一个很有意思的问题

问题代码:

 1 // 1.准备一个集合,排序。
 2         List<Movie> movies = new ArrayList<>();
 3         movies.add(new Movie("摔跤吧,爸爸", 9.5, "阿米尔汗"));
 4         movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
 5         movies.add(new Movie("三傻宝莱坞", 8.5, "阿米尔汗2"));
 6         movies.add(new Movie("阿甘正传", 7.5, "汤姆汉克斯"));
 7 // map加工方法(映射):把流上的数据加工成新数据。
 8         System.out.println("-----------------------------------------------");
 9         //第一次map
10         movies.stream().map( movie-> {
11                 movie.setName("电影:"+movie.getName());
12                 return movie;
13             }
14         );
15         //第二次map 加 foreach
16         movies.stream().map( m -> {
17                 m.setName("黑马:" + m.getName());
18                 return m;
19         }).forEach(System.out::println);
20 21 
22         System.out.println("原始数据" +movies);

输出结果:

Movie{name='黑马:摔跤吧,爸爸', score=9.5, actor='阿米尔汗'}
Movie{name='黑马:三傻宝莱坞', score=8.5, actor='阿米尔汗2'}
Movie{name='黑马:三傻宝莱坞', score=8.5, actor='阿米尔汗2'}
Movie{name='黑马:阿甘正传', score=7.5, actor='汤姆汉克斯'}
原始数据[Movie{name='黑马:摔跤吧,爸爸', score=9.5, actor='阿米尔汗'}, Movie{name='黑马:三傻宝莱坞', score=8.5, actor='阿米尔汗2'}, Movie{name='黑马:三傻宝莱坞', score=8.5, actor='阿米尔汗2'}, Movie{name='黑马:阿甘正传', score=7.5, actor='汤姆汉克斯'}]

 

  • 第一个map()方法中没有使用collect()方法来收集加工后的流,而是直接调用了第二个map()方法。这样会导致第一个map()方法的结果被丢弃,可为什么第二个map()方法加上forEach会改变原始数据呢?

为了弄明白其中缘由我查询了一些资料,究其原因和Stream流的中间操作和终端操作有关

在Java 8中,stream是一种抽象的数据结构,它表示一个元素序列,可以对这些元素进行各种操作,比如过滤、映射、排序、聚合等。stream本身并不存储数据,而是从一个源(比如集合、数组、文件等)获取数据,并按照一定的规则处理数据,然后输出到一个目标(比如另一个集合、数组、文件等)。

stream有两种类型的操作:中间操作和终端操作。中间操作是指返回一个新的stream的操作,比如map、filter、sorted等。终端操作是指返回一个非stream的结果的操作,比如forEach、collect、reduce等。

当我们对一个stream进行中间操作时,并不会立即执行这些操作,而是会创建一个新的stream,并记录下这些操作。只有当我们对这个stream进行终端操作时,才会触发这些中间操作的执行,这种机制称为惰性求值

例如,当我们写下以下代码时: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);  list.stream().map(i -> i * 2); 

并不会立即对list中的每个元素乘以2,而是会返回一个新的stream,并记录下map这个中间操作。只有当我们对这个stream进行终端操作时,比如:

List<Integer> newList = list.stream().map(i -> i * 2).collect(Collectors.toList());

才会触发map这个中间操作的执行,并把结果收集到一个新的列表中。

那么,为什么在后面调用forEach也可以保存修改的对象呢?这是因为forEach是一种特殊的终端操作,它不会返回任何结果,而是对stream中的每个元素执行一个消费者函数(Consumer),这个函数可以对元素进行任何操作,包括修改元素的状态。

例如,当我们写下以下代码时:

List<SampleDTO> list = ...; // 假设list是一个SampleDTO对象的列表
list.stream().forEach(s -> s.setText(s.getText() + "xxx")); // 对每个对象的text属性追加"xxx"

就会触发forEach这个终端操作的执行,并对list中的每个对象执行消费者函数s -> s.setText(s.getText() + “xxx”),这个函数会修改对象的text属性。因此,在执行完这段代码后,list中的每个对象都会被修改。

需要注意的是,虽然forEach可以修改对象的状态,但并不意味着它可以修改stream的源。例如,以下代码是错误的:

List<SampleDTO> list = ...; // 假设list是一个SampleDTO对象的列表
list.stream().forEach(s -> list.remove(s)); // 尝试删除每个对象

这段代码会抛出ConcurrentModificationException异常,因为它试图在遍历list的同时修改list,这是不允许的。

 总之,当我们在后面调用forEach也可以保存修改的对象,是因为forEach是一种特殊的终端操作,它可以对stream中的每个元素执行任何操作,包括修改元素的状态。但是,我们应该避免使用forEach来修改元素的状态,因为这样会破坏函数式编程的原则和可读性。我们应该尽量使用其他终端操作来返回新的结果,而不是修改原来的结果。

标签:map,Stream,stream,Movie,list,终端,操作,forEach
From: https://www.cnblogs.com/FangwayLee/p/17576887.html

相关文章

  • WINPE(Windows Preinstallation Environment)是一个基于Windows操作系统的轻量级预安装
    WINPE(WindowsPreinstallationEnvironment)是一个基于Windows操作系统的轻量级预安装环境。它主要用于系统部署、故障排除、数据恢复和维护等任务。以下是一些常见的WINPE版本:WindowsPE2.0:也称为Vista版,基于WindowsVista操作系统。具有较高的兼容性,并提供了各种工具和驱动程序......
  • 查询docker的操作记录
    查询Docker的操作记录作为一名经验丰富的开发者,我将指导你如何查询Docker的操作记录。在这个过程中,我将提供步骤和相应的代码示例,以帮助你更好地理解。步骤概览以下是查询Docker的操作记录的步骤概览:步骤描述1安装Docker2配置Docker日志驱动3重启Docker守护......
  • .net平台如何切换国产操作系统
    .NET平台如何切换国产操作系统简介在某些特定的应用场景中,我们可能需要将已经开发好的应用程序迁移到国产操作系统上运行,比如麒麟操作系统。本文将介绍如何使用.NET平台切换到国产操作系统的方案,并提供代码示例作为参考。确认国产操作系统兼容性在开始切换操作系统之前,首先需要......
  • 操作系统
    1、操作系统启动过程:①执行BIOS,进行硬件自检并且去磁盘的0号块的0号扇区读取bootsect.s放入内存区域②执行bootsect.s把操作系统的后部分代码读入,并放在相邻位置。包括setup.s、system.s。③执行setup.s,初始化一些数据结构,用于管理硬件。④执行system2、系统调用:①系统调用......
  • VMware 客户机操作系统已禁止CPU。请关闭或重置虚拟机
    系统版本:Win11虚拟机版本:VM16.2.4从其他系统迁移过来的VM虚拟机,启动提示错误。搜好多都解决不了。(图片来源于网络)解决另外一个“无法运行虚拟机”问题时,无意中把这个问题解决了。解决方法:关闭系统安全选项https://blog.csdn.net/tianpeng666/article/details/1292683......
  • 数据库回档操作
    要求:模仿腾讯云数据库,选择数据库,或者数据库下的表,然后点击回档时间,进行回档操作;实现:实现思路通过冷备数据+热备数据实现数据库回档到固定的时间点,前提要求,数据库固定时间都会进行数据备份;冷备数据:冷备方式有多种,物理冷备,逻辑冷备;物理冷备:就是把数据库整个文件拷贝下来,需......
  • Linux防火墙操作
    防火墙操作#查看防火墙窗台systemctlstatusfirewalld\firewall-cmd--state#暂时关闭防火墙systemctlstopfirewalld#永久关闭防火墙systemctldisablefirewalld#开启防火墙systemctlstartfirewalld#开放指定端口firewall-cmd--zone=public--add-port=8......
  • Java操作Redis
    介绍Redis的Java客户端有多种,官方推荐使用的有三种:JedisLettuceRedissonSpring对Redis客户端进行了整合,提供了SpringDataRedis,在SpringBoot项目中还提供了对应的Starter,即spring-boot-starter-data-redis。使用Jedis操作Redis的步骤:获取链接执行操作关闭连接导入......
  • linux 终端 ctrl + c无法终止当前程序
     001、问题linux终端ctrl+c无法终止当前程序 002、解决方法01、ctrl+z:让程序后台运行02、找到该进程03、kill-9该进程名称或者号码 参考:https://blog.csdn.net/m0_67401382/article/details/126434550......
  • Kafka客户端操作
    五类API Kafka客户端API类型AdminClientAPI:允许管理和检测Topic、broker以及其它Kafka对象(类似于命令行的createtopic)ProducerAPI:发送消息到1个或多个TopicConsumerAPI:订阅一个或多个Topic,并处理产生的消息StreamsAPI:高效地将输入流转换到输出流ConnectorAPI:从一......