Java中快速处理集合_简洁又快速地处理集合——Java8 Stream(上)

作者:Howie_Y,系原创投稿

主页:www.jianshu.com/u/79638e5f0743

Java 8 发布至今也已经好几年过去,如今 Java 也已经向 11 迈去,但是 Java 8 作出的改变可以说是革命性的,影响足够深远,学习 Java 8 应该是 Java 开发者的必修课。

今天给大家带来 Java 8 Stream 讲解,为什么直接讲这个,是因为只要你学完,立刻就能上手,并能让它在你的代码中大展身手。

值得注意的是:学习 Stream 之前必须先学习 lambda 的相关知识。本文也假设读者已经掌握 lambda 的相关知识。

本篇文章主要内容:

介绍 Stream 以及 Stream 是如何处理集合的

介绍 Stream 与集合的关系与区别

Stream 的基本方法介绍

一. 什么是 Stream

Stream 中文称为 “流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。

换句话说,你只需要告诉流你的要求,流便会在背后自行根据要求对元素进行处理,而你只需要 “坐享其成”。

二. 流操作

2b476d99f39c24981b36a6f437ff03c7.png

整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理。

其中数据源便是原始集合,然后将如 List

的集合转换为 Stream

类型的流,并对流进行一系列的中间操作,比如过滤保留部分元素、对元素进行排序、类型转换等;最后再进行一个终端操作,可以把 Stream 转换回集合类型,也可以直接对其中的各个元素进行处理,比如打印、比如计算总数、计算最大值等等

很重要的一点是,很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,我们来看看一条 Stream 操作的代码:

4aeaa00c84e43148da003ef19996b02f.png

如果是以前,进行这么一系列操作,你需要做个迭代器或者 foreach 循环,然后遍历,一步步地亲力亲为地去完成这些操作;但是如果使用流,你便可以直接声明式地下指令,流会帮你完成这些操作。

有没有想到什么类似的?是的,就像 SQL 语句一样, select username from user where id = 1,你只要说明:“我需要 id 是 1 (id = 1)的用户(user)的用户名(username )”,那么就可以得到自己想要的数据,而不需要自己亲自去数据库里面循环遍历查找。

三. 流与集合

什么时候计算

Stream 和集合的其中一个差异在于什么时候进行计算。

一个集合,它会包含当前数据结构中所有的值,你可以随时增删,但是集合里面的元素毫无疑问地都是已经计算好了的。

流则是按需计算,按照使用者的需要计算数据,你可以想象我们通过搜索引擎进行搜索,搜索出来的条目并不是全部呈现出来的,而且先显示最符合的前 10 条或者前 20 条,只有在点击 “下一页” 的时候,才会再输出新的 10 条。

再比方在线观看电影和你硬盘里面的电影,也是差不多的道理。

外部迭代和内部迭代

Stream 和集合的另一个差异在于迭代。

我们可以把集合比作一个工厂的仓库,一开始工厂比较落后,要对货物作什么修改,只能工人亲自走进仓库对货物进行处理,有时候还要将处理后的货物放到一个新的仓库里面。在这个时期,我们需要亲自去做迭代,一个个地找到需要的货物,并进行处理,这叫做外部迭代。

后来工厂发展了起来,配备了流水线作业,只要根据需求设计出相应的流水线,然后工人只要把货物放到流水线上,就可以等着接收成果了,而且流水线还可以根据要求直接把货物输送到相应的仓库。

这就叫做内部迭代,流水线已经帮你把迭代给完成了,你只需要说要干什么就可以了(即设计出合理的流水线)。

Java 8 引入 Stream 很大程度是因为,流的内部迭代可以自动选择一种合适你硬件的数据表示和并行实现;而以往程序员自己进行 foreach 之类的时候,则需要自己去管理并行等问题。

一次性的流

流和迭代器类似,只能迭代一次。

Stream stream = list.stream().map(Person::getName).sorted().limit(10);

List newList = stream.collect(toList());

List newList2 = stream.collect(toList());

上面代码中第三行会报错,因为第二行已经使用过这个流,这个流已经被消费掉了

四. 方法介绍,开始实战

首先我们先创建一个 Person 泛型的 List

List list = new ArrayList<>();

list.add(new Person("jack", 20));

list.add(new Person("mike", 25));

list.add(new Person("tom", 30));

Person 类包含年龄和姓名两个成员变量

private String name;

private int age;

1. stream() / parallelStream()

最常用到的方法,将集合转换为流

List list = new ArrayList();

// return Stream

list.stream();

而 parallelStream() 是并行流方法,能够让数据集执行并行操作,后面会更详细地讲解

2. filter(T -> boolean)

保留 boolean 为 true 的元素

保留年龄为 20 的 person 元素

list = list.stream()

.filter(person -> person.getAge() == 20)

.collect(toList());

打印输出 [Person{name='jack', age=20}]

collect(toList()) 可以把流转换为 List 类型,这个以后会讲解

3. distinct()

去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的

如例子中的 Person 类,需要先定义好 equals 方法,不然类似[Person{name='jack', age=20}, Person{name='jack', age=20}] 这样的情况是不会处理的

4. sorted() / sorted((T, T) -> int)

如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,如 Stream

反之, 需要调用 sorted((T, T) -> int) 实现 Comparator 接口

根据年龄大小来比较:

list = list.stream()

.sorted((p1, p2) -> p1.getAge() - p2.getAge())

.collect(toList());

当然这个可以简化为

list = list.stream()

.sorted(Comparator.comparingInt(Person::getAge))

.collect(toList());

5. limit(long n)

返回前 n 个元素

list = list.stream()

.limit(2)

.collect(toList());

打印输出 [Person{name='jack', age=20}, Person{name='mike', age=25}]

6. skip(long n)

去除前 n 个元素

list = list.stream()

.skip(2)

.collect(toList());

打印输出 [Person{name='tom', age=30}]

tips:

用在 limit(n) 前面时,先去除前 m 个元素再返回剩余元素的前 n 个元素

limit(n) 用在 skip(m) 前面时,先返回前 n 个元素再在剩余的 n 个元素中去除 m 个元素

list = list.stream()

.limit(2)

.skip(1)

.collect(toList());

打印输出 [Person{name='mike', age=25}]

7. map(T -> R)

将流中的每一个元素 T 映射为 R(类似类型转换)

List newlist =

list.stream().map(Person::getName).collect(toList());

newlist 里面的元素为 list 中每一个 Person 对象的 name 变量

8. flatMap(T -> Stream

)

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

List list = new ArrayList<>();

list.add("aaa bbb ccc");

list.add("ddd eee fff");

list.add("ggg hhh iii");

list = list.stream().map(s -> s.split(" ")).

flatMap(Arrays::stream).collect(toList());

上面例子中,我们的目的是把 List 中每个字符串元素以" "分割开,变成一个新的 List

首先 map 方法分割每个字符串元素,但此时流的类型为 Stream

,因为 split 方法返回的是 String[ ] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream

流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 Stream

9. anyMatch(T -> boolean)

流中是否有一个元素匹配给定的 T -> boolean 条件

是否存在一个 person 对象的 age 等于 20:

boolean b = list.stream().anyMatch(person -> person.getAge() == 20);

10. allMatch(T -> boolean)

流中是否所有元素都匹配给定的 T -> boolean 条件

11. noneMatch(T -> boolean)

流中是否没有元素匹配给定的 T -> boolean 条件

12. findAny() 和 findFirst()

findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)

findFirst():找到第一个元素

值得注意的是,这两个方法返回的是一个 Optional

对象,它是一个容器类,能代表一个值存在或不存在,这个后面会讲到

13. reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

用于组合流中的元素,如求和,求积,求最大值等

计算年龄总和:

int sum = list.stream().map(Person::getAge).reduce(0, (a, b) -> a + b);

与之相同:

int sum = list.stream().map(Person::getAge).reduce(0, Integer::sum);

其中,reduce 第一个参数 0 代表起始值为 0,lambda (a, b) -> a + b 即将两值相加产生一个新值。

同样地:

计算年龄总乘积:

int sum = list.stream().map(Person::getAge).reduce(1, (a, b) -> a * b);

当然也可以

Optional sum = list.stream().map(Person::getAge).reduce(Integer::sum);

即不接受任何起始值,但因为没有初始值,需要考虑结果可能不存在的情况,因此返回的是 Optional 类型。

13. count()

返回流中元素个数,结果为 long 类型

14. collect()

收集方法,我们很常用的是 collect(toList()),当然还有 collect(toSet()) 等,参数是一个收集器接口,这个后面会另外讲。

15. forEach()

返回结果为 void,很明显我们可以通过它来干什么了,比方说:

### 16. unordered()还有这个比较不起眼的方法,

#返回一个等效的无序流,当然如果流本身就是无序的话,那可能就会直接返回其本身

打印各个元素:

list.stream().forEach(System.out::println);

再比如说 MyBatis 里面访问数据库的 mapper 方法:

向数据库插入新元素:

list.stream().forEach(PersonMapper::insertPerson);

推荐大而全的【后端技术精选】

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

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

相关文章

eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件

导读&#xff1a; 最近在看 Flink 源码的时候发现到一段实用的代码&#xff0c;该代码实现了 java 动态编译以及生成 jar 文件。将其进行改进后可以应用到我们的平台上&#xff0c;实现在平台页面上编写 java 代码语句&#xff0c;提交后由后台进行编译和打成 Jar 包再上传到指…

Dx11DemoBase 基类(三) 实例应用 【已实现】【附带源码】

现在我已经到哪了? 读书时&#xff0c;尤其是技术知识书籍&#xff0c; 我一般会担忧自己是否陷得太深&#xff0c; 细节关注得太多&#xff0c; 而忘了整体的过程&#xff1b; 一直以来对Direct3D 很畏惧&#xff0c; 因为太多函数和细节&#xff1b;现在我必须暂缓下&#x…

修改 decimal 默认值为0.00 sql_被经理邀请去“爬山”,只是因为我写错了一条SQL语句?...

作者&#xff1a;isysc1链接&#xff1a;https://juejin.im/post/5f06a2156fb9a07e5f5180df来源&#xff1a;掘金前戏SQL 写的妙&#xff0c;涨薪呱呱叫&#xff01;新来的实习生小杨写了一条 SQL 语句SELECT wx_id from user WHERE wx_id 2当小杨迫不及待准备下班回家的时候&…

JS中关于clientWidth、offsetWidth、scrollWidth

网页可见区域宽&#xff1a; document.body.clientWidth;网页可见区域高&#xff1a; document.body.clientHeight;网页可见区域宽&#xff1a; document.body.offsetWidth (包括边线的宽);网页可见区域高&#xff1a; document.body.offsetHeight (包括边线的宽);网页正文全…

shell 执行失败重试_Uipath 机器人总是运行失败怎么办?

要知道为什么RPA机器人容易失败&#xff0c;首先了解下它和常规的应用系统有哪些区别。常规应用系统&#xff0c;就像程序员自己创造了一个世界、一个域&#xff0c;在这个世界里创造它的人就是主宰。出现BUG的风险是相对可控的&#xff0c;顶多是功能用不了。而RPA项目&#x…

c mysql安装教程视频_MySQL安装教程 - Windows安装MySQL教程 - 小白式安装MySQL教程 - 青衫慧博客...

版权声明本文转发自旧站点萧瑟云日志&#xff0c;近期考虑准备将旧站进行关闭(没有精力维护)&#xff0c;部分文章将会迁移至本站。文章发表于&#xff1a;2017-10-28 12:32:03前言上次给大家带来了SQL Server的小白式安装教程&#xff0c;这次再次带来一个MySQL的小白式安装教…

PJSIP UA分析(1)--PJSUA主函数

1 intmain(intargc, char*argv[])2 {3 do{4 app_restart PJ_FALSE; //PJ_FALSE是一个宏&#xff0c;一旦用户调用pjsua可执行文件进入该循环&#xff0c;那么默认只执行一次退出5 //如果需要再次循环&#xff0c;那么在下面函数中…

锁定表头和固定列(Fixed table head and columns)

前段时间需要这个功能&#xff0c;但是找了很多都不能完美的实现&#xff0c;不是只能锁定表头&#xff0c;就是浏览器兼容问题什么的&#xff0c;在此就自己做了一个锁定表头和列的js方法&#xff0c;依赖于JQuery。 因为方法很简单&#xff0c;就未封装成插件的形式&#xff…

游戏详细设计说明书_宜家的说明书设计脑洞太大了!

平常我们看到的说明书是像这样纯文字解说的或者是规范的文字配图这些说明书一般都是注重文字的上表达而大家熟悉的家居品牌宜家将说明书创意玩出了新境界&#xff01;↓↓↓这不&#xff0c;最近由于全球疫情严峻期间宜家的全新说明书手册搜罗了各种纸上游戏意为帮助大家打发无…

centos删除php_centos如何卸载php

查看php版本php -v查看php相关软件包#rpm -qa|grep php(视频教程推荐&#xff1a;linux视频教程)提示如下&#xff1a;#php-pdo-5.1.6-27.el5_5.3#php-mysql-5.1.6-27.el5_5.3#php-xml-5.1.6-27.el5_5.3#php-cli-5.1.6-27.el5_5.3#php-common-5.1.6-27.el5_5.3#php-gd-5.1.6-27…

cgblib 代理接口原理_Java开发者你还不知道?告诉你Dubbo 的底层原理,面试不再怕...

前言平常我们在构建分布式系统的时候&#xff0c;一般都是基于 Dubbo 技术栈或者是SpringCloud 技术栈来做。早期其实最先比较流行的是Dubbo&#xff0c;我记得我们当时有个部分的老大就是用的是Dubbo 来构建的一个系统&#xff0c;到后面才出来的 SpringCloud&#xff0c;由于…

包含对流环热,热流边界,等温边界的稳态热传导方程的FEM求解。

以下面的问题为例&#xff1a;对于如图所示的平面传热问题&#xff0c; 若上端有给定的热流-2W/m2&#xff0c;即从下往上传输热量&#xff0c;结构下端有确定的温度100&#xff0c;周围介质温度为20&#xff0c;在两侧有换热&#xff0c;换热系数为α100W/㎡/K&#xff0c;热导…

python生成动态二维码实例_python生成动态个性二维码(示例代码)

1 安装工具 2 生成普通二维码 3 带图片的二维码 4 动态 GIF 二维码 5 在Python程序中使用 一、安装 首先在python环境下运行&#xff0c; 打开cmd进入python27 进入scripts 然后在scripts输入命令&#xff1a;pip install myqr二、 生成普通二维码 安装了 myqr 之后&#xff0c…

matlab武汉理工大学数值分析线性函数拟合实验_「首席架构师推荐」数值计算库精选...

这是一个著名的数值库列表&#xff0c;这些库用于软件开发中执行数值计算。它不是一个完整的列表&#xff0c;而是一个包含Wikipedia上文章的数字库列表&#xff0c;很少有例外。典型库的选择取决于一系列不同的需求&#xff0c;例如:期望的特性(例如:大维线性代数、并行计算、…

JSONP跨域原理和jQuery.getJSON用法

JSONP是一个非官方的协议&#xff0c;它允许在服务器端集成Script tags返回至客户端&#xff0c;通过javascript callback的形式实现跨域访问&#xff08;这仅仅是JSONP简单的实现形式&#xff09;。本文主要介绍JSONP跨域原理&#xff0c;一起来看。 JSONP是一个非官方的协议&…

串口输出5v电压_为什么RS485比串口速度快距离远?--谈单端信号与差分信号之差异...

嵌入式系统中&#xff0c;串口、RS485、CAN、网络和USB等都是非常常用的通信方式。但是串口通信速度慢&#xff0c;距离近&#xff0c;为什么转换成RS485后&#xff0c;通信距离和速度都大幅提高了呢&#xff1f;USB也是近距离&#xff0c;为什么速度可以这么快&#xff1f;原因…

IIS7.0站点/虚拟目录中访问共享

目的&#xff1a;实现一个2008serve的IIS的虚拟目录&#xff08;通过网络路径&#xff08;UNC&#xff09;的形式&#xff0c;共享在另外一个2008服务器上&#xff09; 准备工作1.运行组策略编辑器&#xff08;gpedit.msc&#xff09;&#xff1b;找到本地安全策略-本地策略-安…

易语言操作php文本文件,易语言对文本操作的步骤教学

在易语言编程中&#xff0c;我们往往需要对一些文字进行截取或分割出来&#xff0c;如何准确、快速的实现这一目标呢&#xff1f;下面笔者来为大家演示1、首先&#xff0c;我们打开易语言编程软件&#xff0c;点击左上角&#xff0c;新建一个文件&#xff0c;如图所示2、我们点…

xxl-job 执行结果是空_xxljob dotnet core executor执行器开源

DotXxlJob[(github)https://github.com/xuanye/DotXxlJob][https://github.com/xuanye/DotXxlJob] xxl-job的dotnet core 执行器实现&#xff0c;支持XXL-JOB 2.01 XXL-JOB概述[XXL-JOB][1]是一个轻量级分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量…

php5.4 windows2003,PHP实战:Windows2003下php5.4安装配置教程(IIS)

《PHP实战&#xff1a;Windows2003下php5.4安装配置教程(IIS)》要点&#xff1a;本文介绍了PHP实战&#xff1a;Windows2003下php5.4安装配置教程(IIS)&#xff0c;希望对您有用。如果有疑问&#xff0c;可以联系我们。PHP教程一、在Windows2003安装IISPHP教程1、首先打开Windo…