JAVASE进阶:一文精通Stream流+函数式编程

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:JAVASE进阶:源码精读——HashMap源码详细解析
📚订阅专栏:JAVASE进阶
希望文章对你们有所帮助

Stream流的使用是一种高级的写法,配合函数式编程(lambda表达式),能够极大简化我们程序的编写,有些二十行的代码也可以一行代码就实现,代码看起来也高雅了很多,这也是成为高级程序员的必会技能。

先讲原理,再实现一些例子。

一文精通Stream流+函数式编程

  • 引入
  • Stream流思想
  • 获取Stream流
  • Stream流的中间方法
  • Stream流终结方法详解
  • 收集方法collect
  • 练习
    • 数字过滤
    • 字符串过滤并收集
    • 自定义对象过滤并收集

引入

现在实现一个简单的需求:创建一个集合来存储名字,并且输出名字长度为3,且姓为“张”的所有名字。
这里直接用ArrayList,模拟起来还是很简单的,但是当相对还是要写很长的语句,而Stream流和Lambda表达式的集合能够极大地非常简化我们的代码编写,处理集合并输出的语句只需要一句话:

list.stream().filter(name->name.startWith("张")).filter(name->name.length()==3).forEach(name->System.out.println(name));

这代码对一个程序员的诱惑程度别提有多大。

Stream流思想

流:可以视为流水线,学过计算机系统都知道流水线的相关原理,它可以大大提升执行的性能。
如上例子可以分为流水线中的3个子任务:过滤留下“张”姓开头的;过滤留下长度为3的;输出。
对于每一个子任务,Stream流一般都会结合lambda表达式来简化集合、数组的操作。

Stream流的使用步骤:

1、先得到一条Stream流(流水线),并把数据放上去
2、利用Stream流的API进行各种操作:
(1)中间方法:过滤、转换
(2)终结方法:统计、打印

获取Stream流

获取方式方法名说明
单列集合stream()Collection中的默认方法
双列集合无法直接使用是Stream流,需要先通过keySet或entrySet转换成单列集合
数组stream(T[] array)Arrays工具类中的静态方法
一堆零散数据of(T…values)Stream接口中的静态方法

1、单列集合

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d");
//Stream<String> stream = list.stream()可以获取流,但一般不这么用,而是直接一路链式编程
list.stream().forEach(s->System.out.println(s));//forEach是终结方法

2、双列集合

HashMap<String, Integer> map = new HashMap<>();
map.put("aaa", 111);map.put("bbb", 222);
//方法一,把key都拿出来单独做处理
map.keySet().stream().forEach(s->System.out.println(s));
//方法二,获取每个键值对对象
map.entrySet().stream().forEach(s->System,out.println(s));

3、数组

int[] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(s->System,out.println(s));

4、零散数据

Stream.of(1,2,3,4,5).forEach(s->System,out.println(s));
Stream.of("a","b","c").forEach(s->System,out.println(s));

在很多时候,都是推荐使用of方法,这是因为of方法形参写法为T...values,这是一种可变参数的书写形式,所以无论传递零散的数据还是一个数组,都是可以成功处理的。

但是!有一个很关键的点,of方法传入的数组里面的数据必须是引用数据类型的,如果是基本数据类型,那么只会把这一整个数组当作一个元素,输出的会是地址!

Stream流的中间方法

名称说明
filter过滤
limit获取前几个元素
skip跳过前几个元素
distinct元素去重,依赖hashCode和equals方法
concat合并a和b两个流为一个流
map转换流中的数据类型

注意:

1、中间方法会返回新的流,原来的Stream流只能使用一次,建议使用链式编程
2、修改Stream流中的数据,不会影响原来集合或数组中的数据

1、filter:
(1)匿名内部类

list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s){//返回值为true则表示当前数据留下return s.startWith("张");}
}).forEach(s->System.out.println(s));

(2)lambda表达式:

list.stream().filter(s->s.startWith("张")).forEach(s->System.out.println(s));

2、limit:

//输出前3个
list.stream().limit(3).forEach(s->System.out.println(s));

3、skip:

//跳过前4个
list.stream().skip(4).forEach(s->System.out.println(s));

4、distinct:

list.stream().distinct().forEach(s->System.out.println(s));

distinct的底层是非常复杂的,其核心是使用HashSet实现去重的

5、concat尽可能让两个stream流中的数据保持一致,不然会将数据类型变成它们的共同父类,导致缺失一些子类的特有功能:

Stream.concat(list1.stream(), list2.stream()).forEach(s->System.out.println(s));

6、map实现流中数据的类型转换,将String类型字符串中的年龄转换成int并输出:

Collections.addAll(list, "一二-12", "布布-14");

(1)匿名内部类:

list.stream().map(new Function<String, Integer>(){//String表示stream中的数据类型,Integer表示要转换的数据类型,不能写int,必须写包装类,默认为Object@Overridepublic Integer apply(String s){//s表示stream流里的每一个数据,返回值为转换后的数据String[] arr = s.split("-");String ageString = arr[1];int age = Integer.parseInt(ageString);return age;}
}).forEach(s->System.out.println(s));

(2)lambda表达式:

list.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(s->System.out.println(s));

这个lambda表达式写起来剪枝不要太爽!

Stream流终结方法详解

名称说明
forEach遍历
count统计
toArray收集流中数据,放到数组中
collect收集流中数据,放到集合中

1、forEach:
(1)匿名内部类

list.stream().forEach(new Consumer<String>(){@Overridepublic void accept(String s){System.out.println(s)}
});

(2)lambda表达式

list.stream().forEach(s->System.out.println(s));

2、count统计:

long count = list.stream().count();

3、toArray收集int类型数据到String类型数组中
(1)匿名内部类

//注意IntFunction中要传递数组泛型,所以需要传入"String[]"
String[] arr = list.stream().toArray(new new IntFunction<String[]>() {@Overridepublic void apply(int value){//value表示数组的长度,方法体只需要创建一个数组即可,返回的类型要和泛型一致return new String[value];}
});
System.out.println(Arrays.toString(arr));

(2)lambda表达式

String[] arr = list.stream().toArray(value->new String[value]);
System.out.println(Arrays.toString(arr));

collect方法算是比较重要的方法了,在后面详细讲解。

收集方法collect

collect可以将流里面的数据收集到单列集合或双列集合中去的。

收集到List集合:

	List<String> list = new ArrayList<>();Collections.addAll(list, "张无忌-男-15", "小龙女-女-12", "一二-女-4", "布布-男-5");//把所有的男性收集起来List<String> newList = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toList());System.out.println(newList);

同样的,也可以收集到Set中去,单列集合还是比较方便的。

如果要收集到双列集合Map中,就需要指定键和值是哪个字段:

可以打开ToMap的底层源码,可以清楚的看到,调用toMap方法需要指定键和值的规则,其底层会自动的创建一个HashMap对象:
在这里插入图片描述
收集到Map集合里面的代码和细节如下所示,需要注意的是,要想收集Map必须保证键是不重复的。

(1)匿名内部类:

	//键:姓名 值:年龄Map<String, Integer> map = list.stream().filter(s -> "男".equals(s.split("-")[1]))/*** toMap:参数一表示键的生成规则*       参数二表示值的生成规则**  参数一:*      Function泛型一:流中每一个数据的类型*              泛型二:Map集合中键的数据类型*      方法apply形参:依次表示流里面的每一个数据*              方法体:生成键的代码*              返回值:已经生成的键*  也就是说,Function中第一个泛型与apply的形参一致,第二个泛型与apply的返回类型一致**  参数二同理*/.collect(Collectors.toMap(new Function<String, String>() {@Overridepublic String apply(String s) {return s.split("-")[0];}},new Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s.split("-")[2]);}}));System.out.println(map);

(2)lambda表达式

	Map<String, Integer> map = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[2])));

练习

数字过滤

定义一个集合并添加整数1-10,过滤奇数只留下偶数,并将结果保存起来:

ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> list1 = list.stream().filter(s -> (s & 1) == 0).collect(Collectors.toList());
System.out.println(list1);

字符串过滤并收集

创建一个ArrayList集合,并添加以下字符串:

“zhangsan,23”
“lisi,24”
“wangwu,25”

保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值:

	public static void main(String[] args) {String s1 = "a", s2 = "b";//System.out.println(s1.compareTo(s2));ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25");//filter也可以转换成Integer类型再去做比较Map<String, Integer> map = list.stream().filter(s -> "24".compareTo(s.split(",")[1]) <= 0).collect(Collectors.toMap(s -> s.split(",")[0],s -> Integer.parseInt(s.split(",")[1])));System.out.println(map);}

自定义对象过滤并收集

在这里插入图片描述
自行去创建一个Actor对象,String类型的name、int类型的age,并且重写toString方法。

代码如下:

	public static void main(String[] args) {ArrayList<String> manList = new ArrayList<>();ArrayList<String> womenList = new ArrayList<>();Collections.addAll(manList, "坤坤,24", "再多,23", "看一眼,20", "就快,19", "要爆炸,22", "铁山靠,26");Collections.addAll(womenList, "朵拉,21", "杨幂,30", "杨超越,22", "露娜,25", "妲己,24", "安琪拉,23");Stream<String> stream1 = manList.stream().filter(s -> s.split(",")[0].length() == 3).limit(2);Stream<String> stream2 = womenList.stream().filter(s->s.startsWith("杨")).skip(1);List<Actor> list = Stream.concat(stream1, stream2).map(new Function<String, Actor>() {@Overridepublic Actor apply(String s) {return new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]));}}).collect(Collectors.toList());}System.out.println(list);

其中,map里面的匿名内部类还可以转化为lambda表达式:

s->new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/671327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万物皆可问 — 私有部署网易有道QAnything

什么是 QAnything&#xff1f; QAnything&#xff08;Question and Answer based on Anything&#xff09;是一个本地知识库问答系统&#xff0c;旨在支持多种文件格式和数据库&#xff0c;允许离线安装和使用。使用QAnything&#xff0c;您可以简单地删除本地存储的任何格式的…

利用Excel爬取网页数据

想要获取网页上的表格数据&#xff0c;可以通过Excel自带的功能&#xff0c;从网站导入数据&#xff0c;并且可以实时刷新最新数据。具体步骤如下&#xff1a; 1、新建Excel&#xff0c;打开&#xff0c;选择【数据】-【自网站】 2、在弹出的对话框中输入目标网址&#xff0c;…

子集枚举介绍

集合枚举的意思是从一个集合中找出它的所有子集。集合中每个元素都可以被选或不选&#xff0c;含有n个元素的集合总共有个子集&#xff08;包括全集和空集&#xff09; 例如考虑集合和它的4个子集、、、&#xff0c;按照某个顺序&#xff0c;把全集A中的每个元素在每个子集中的…

LeetCode:9.回文数,对整数的反转操作

博主本想找个简单的题水一下&#xff0c;结果太久没写这块的代码&#xff0c;直接写着宕机着&#xff0c;十分难受&#xff0c;最后还调试了几下&#xff0c;悲&#xff0c; 目录 题目&#xff1a; 思路&#xff1a; 官方代码&#xff08;反转一半的&#xff09;&#xff1a…

酷开科技,打造非凡的生活体验

酷开科技&#xff0c;作为一家专注于智能电视操作系统研发及智能电视运营增值服务的高科技企业&#xff0c;始终致力于为消费者提供非凡的生活体验。通过不断创新的技术和产品&#xff0c;酷开科技为消费者们带来了便捷、舒适、个性化的智能生活。 首先&#xff0c;酷开科技在智…

【Linux笔记】文件系统与软硬链接

一、文件系统概述 1.1、先来聊一聊“磁盘” 在讲解文件系统之前&#xff0c;我觉得有必要先聊一下“磁盘”&#xff0c;因为我觉得如果弄懂了磁盘的存储原理&#xff0c;大家可能更容易理解文件系统是怎么管理数据的&#xff0c;并且理解计算机是怎么将磁盘抽象到文件系统的。…

unity实现第一人称和第三人称

在角色设置两个挂载点&#xff0c;第一人称时&#xff0c;相机放在eys上面&#xff0c;切换第三人称时&#xff0c;放置到3rd节点上面&#xff0c;调整节点位置&#xff0c;达到期望效果 代码 void ThirdView(){Debug.Log("切换到第三人称");camera.SetParent(third…

SaperaCamExpert(相机专家)中文使用指南

参考&#xff1a;SaperaCamExpert中文使用指南.PDF 文章目录 软件介绍安装首次打开资源占用率功能主界面布局菜单栏FileViewPre-Processing&#xff1a;预处理 Tools&#xff1a; 快捷键&#xff1a;新建&#xff1b;打开&#xff1b;保存&#xff1b;帮助Device窗体属性树图像…

【MATLAB】使用梯度提升树在回归预测任务中进行特征选择(深度学习的数据集处理)

1.梯度提升树在神经网络的应用 使用梯度提升树进行特征选择的好处在于可以得到特征的重要性分数&#xff0c;从而识别出对目标变量预测最具影响力的特征。这有助于简化模型并提高其泛化能力&#xff0c;减少过拟合的风险&#xff0c;并且可以加快模型训练和推理速度。此外&…

莉莉与神奇花朵的冒险

现在&#xff0c;我将根据这些步骤编写一个对话形式的童话故事。 在很久很久以前的一个小村庄里&#xff0c;有一个勤劳善良的小女孩叫莉莉。她住在一间小茅屋里&#xff0c;和她的奶奶一起生活。奶奶年纪大了&#xff0c;行动不便&#xff0c;所以莉莉每天都要照顾她。 一天&a…

springboot war包部署 和jar包部署

文章目录 war包部署设置打包方式为war排除内嵌的tomcat在插件中指定入口类打包测试 jar包部署设置打包方式执行打包测试访问修改插件版本指定jsp打包配置 重新打包测试 war包部署 设置打包方式为war 执行项目打包的方式为 "war" 默认创建springboot项目打包都是ja…

vscode的ssh忽然连不上服务器:远程主机可能不符合glibc和libstdc++ VS Code服务器的先决条件

vscode自动更新了一下就发现连不上服务器了&#xff0c;我寻思估计一大堆人都寄了&#xff0c;一搜&#xff0c;果然哈哈哈哈 然后我直接搜一天内新发布的博客&#xff0c;还真给我搜到了这个问题&#xff0c;按照这个问题里面的回答&#xff08;vscode1.86无法远程连接waitin…

2024.2.6日总结(小程序开发3)

页面配置 页面配置和全局配置的关系&#xff1a; 小程序中&#xff0c;app.json中的window节点&#xff0c;可以全局配置小程序中每个页面的窗口表现 如果某些小程序想要有特殊的窗口表现&#xff0c;可以用页面级别的.json配置文件实现这个需求 页面配置和全局配置冲突时&…

程序员为什么不喜欢关电脑?

目录 标题&#xff1a;程序员为何乐见电脑长时间处于关闭状态&#xff1f; 引言&#xff1a; 一、思维的延续性&#xff1a; 二、环境的连续性&#xff1a; 三、长时间开机的原因&#xff1a; 四、恢复成本的考量&#xff1a; 结论&#xff1a; 特别的&#xff1a; 不是…

亿某通电子文档安全管理系统 UploadFileToCatalog SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

Python __file__属性:查看模块的源文件路径

除可以查看模块的帮助信息之外&#xff0c;还可以直接阅读模块的源代码来掌握模块功能&#xff0c;提升 Python 编程能力。 不管学习哪种编程语言&#xff0c;认真阅读那些优秀的框架、库的源代码都是非常好的学习方法。 通过模块的 __file__ 属性即可查看到指定模块的源文件…

防范恶意勒索攻击!亚信安全发布《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件81起&#xff0c;事件数量有所下降&#xff0c;比上月降低20%。 lockbit3.0仍然是影响最严重的勒索家族&#xff1b;akira和incransom也是两个活动频繁的恶意家族&#xff0c;需要注意防范。 本周alphv勒索组织窃取MBC法律专业公司…

LangChain 最近发布的一个重要功能:LangGraph

LangGraph 是 LangChain 最近发布的一个重要功能&#xff0c;LangChain 进入多代理框架领域。通过建立在LangChain 之上&#xff0c;LangGraph 使开发人员可以轻松创建强大的代理运行时。 LangChain 使用其表达语言&#xff08;LCEL&#xff09;为开发人员构建定制链提供技术支…

基于ESP-WROOM-32的双串口通信并显示到OLED显示屏上

目录 开发板引脚图 Arduino环境配置1.ESP32开发版下载2.Arduino开发板选择 -> ESP32 Dev Module3.安装驱动库 接线图Arduino代码现象演示 开发板 ESP-WROOM-32 引脚图 Arduino环境配置 1.ESP32开发版下载 选择 esp32 by Espressif Systems 2.Arduino开发板选择 -> E…

网络爬虫,使用存放在C的谷歌驱动报错

月 06, 2024 11:43:40 上午 org.openqa.selenium.os.OsProcess checkForError 严重: org.apache.commons.exec.ExecuteException: Execution failed (Exit value: -559038737. Caused by java.io.IOException: Cannot run program "C:\chromedriver121.exe" (in dir…