【java基础】Stream流的各种操作
文章目录
- 基本介绍
- 流的创建
- 流的各种常见操作
- forEach方法
- filter方法
- map方法
- peek方法
- flatMap方法
- limit和skip方法
- distinct方法
- sorted方法
- 收集结果
- 收集为数组(toArray)
- 收集为集合(collect)
- 收集为Map
- 关于流的一些说明(终结操作)
- 总结
基本介绍
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
与集合相比,流提供了一种可以让我们在更高概念级别上指定计算任务的数据视图
注意:学习Stream必须要十分清晰的了解lamdba表达式,如果lambda不清楚,请参考一篇文章彻底搞懂lambda表达式
流的创建
我们在学习流之前,应当先了解一下Stream这个类,Stream类的类图和方法如下


对于创建流,我们可以使用静态方法Stream.of来创建

该方法传入一个可变长度的参数,然后就会返回对应类型的流
Stream<Integer> stream = Stream.of(2, 3, 1, 4);
如果要创建一个不包含任何元素的流,可以使用Stream.empty
Stream<Object> empty = Stream.empty();
我们还可以使用Stream的静态方法generate和iterate来创建无限流。
下面就通过generate创建了一个获取随机数的流

Stream<Double> randomNumStream = Stream.generate(Math::random);
如果想要创建例如0,1,2,3这样有规律的序列流,那么就可以使用iterate方法

Stream<Integer> iterate = Stream.iterate(0, num -> num+1);
除了上面几个静态方法,对于流的创建还有许多方法,例如Arrays.stream方法

在Collection中有stream方法和parallelStream方法都可以返回一个Stream流,这也就说明了所有的集合都可以调用这2个方法返回对应的流

对于流,我们有几点注意事项如下
- 流并不存储元素,这些元素可能存储在底层的集合中,或者是按需生成的
- 流的操作不会改变其数据源
- 流的操作尽可能惰性执行。这意味着直至需要其结果时,操作才会执行
流的各种常见操作
这里主要介绍在流里面使用频率较高的几个操作,每个方法都会给出该方法的源注释,以及基本使用,请参考注释和代码来进行理解
forEach方法
这个方法可以对流里面的每一个元素执行操作

Stream<Integer> stream = Stream.of(2, 3, 1, 4);stream.forEach(System.out::println);
输出结果如下
2
3
4
1
filter方法
该方法可以过滤掉流中不满足要求的元素,会返回一个新流

Stream<Integer> stream = Stream.of(2, 3, 1, 4);Stream<Integer> newStream = stream.filter(num -> num > 2);System.out.print("过滤之后:");newStream.forEach(x -> System.out.print(x + " "));
上面代码输出如下
过滤之后:3 4
map方法
当我们想要按照某种方式来转换流中的值的时候,我们就可以使用map

Stream<Integer> stream = Stream.of(2, 3, 1, 4);Stream<Integer> newStream = stream.map(num -> num + 1);newStream.forEach(System.out::println);
上面代码输出如下
3
4
2
5
peek方法
该方法可以对流中的每一个元素进行操作,返回新的流

public class Dog {public String name;public Integer age;public Dog(String name, Integer age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}
Dog[] dogs = {new Dog("tom", 1),new Dog("旺财", 2)};Stream<Dog> dogStream = Arrays.stream(dogs);Stream<Dog> newDogStream = dogStream.peek(dog -> dog.age = 999);newDogStream.forEach(System.out::println);
上面代码输出如下
Dog{name='tom', age=999}
Dog{name='旺财', age=999}
flatMap方法
该方法产生一个流,它是通过将传入lambda表达式应用于当前流中所有元素所产生的结果连接到一起而获得的。(注意,这里的每个结果都是一个流。)

List<Stream<Integer>> streamList = new ArrayList<>();Stream<Integer> stream1 = Stream.of(1, 2, 3);Stream<Integer> stream2 = Stream.of(4, 5, 6);Stream<Integer> stream3 = Stream.of(7, 8, 9);streamList.add(stream1);streamList.add(stream2);streamList.add(stream3);Stream<Stream<Integer>> stream = streamList.stream();// flatMap里面的lambda表达式应当返回一个流Stream<Integer> integerStream = stream.flatMap(x -> x);integerStream.forEach(System.out::println);
上面代码输出如下
1
2
3
4
5
6
7
8
9
limit和skip方法
limit方法可以对流进行裁剪,只取前n个流,skip方法则是跳过前n个流


由于limit和skip用法基本由于,这里就用limit作为例子
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);Stream<Integer> newStream = stream.limit(4);newStream.forEach(System.out::println);
上面代码输出如下
1
2
3
4
distinct方法
这个方法相当于去重

Stream<Integer> stream = Stream.of(1, 2, 3, 3, 1, 4);Stream<Integer> newStream = stream.distinct();newStream.forEach(System.out::println);
代码输出如下
1
2
3
4
sorted方法
这个方法一看就知道是排序用的

该方法有2个,一个带有Comparator,就是用于指定排序方式的
Stream<Integer> stream = Stream.of(4, 1, 3, 2);Stream<Integer> newStream = stream.sorted();newStream.forEach(System.out::println);
代码输出如下
1
2
3
4
收集结果
在上面我们都只是对流进行操作,现在来讲解下如何将流里面的数据收集到集合中。
收集为数组(toArray)
我们可以调用流里面的toArray方法,传入对应的类型数组即可

Stream<String> namesStream = Stream.of("tom", "luck", "jerry");String[] names = namesStream.toArray(String[]::new);System.out.println(Arrays.toString(names));
上面代码输出如下
[tom, luck, jerry]
收集为集合(collect)
调用stream里面的collect方法,然后传入指定的Collector实例即可,Collector提供了大量用于生成常见收集器的工厂方法。
Collector类的方法如下


可以发现有很多方法,这里先介绍几个常用的,其他的方法在后面文章中进行说明。
List<Integer> nums = Arrays.asList(1,2,3,1,4);
toList()可以将结果收集为List
List<Integer> list = nums.stream().collect(Collectors.toList());
toSet()可以将结果收集为Set
Set<Integer> set = nums.stream().collect(Collectors.toSet());
toCollection()可以指定收集的集的种类
TreeSet<Integer> treeSet = nums.stream().collect(Collectors.toCollection(TreeSet::new));
在Collector这个类里面还有其他的很多方法,建议大家去看看这个类的文档,对每个方法都有个影响,需要用到某种操作的时候查找文档即可。
收集为Map
Map也是集合,但是Map收集要比如List,Set等要麻烦一点,所以这里单独说明一下,toMap方法如下

我们需要指定k和v是什么,其实就是对于每一个元素,用什么来作为k和v
Stream<String> namesStream = Stream.of("tom", "jack", "lucy");Map<Character, String> namesMap = namesStream.collect(Collectors.toMap(k -> k.charAt(0), v -> v.toUpperCase()));System.out.println(namesMap);
上面代码就用字符串的第一个字符作为k,然后用字符串的大写作为v。上面代码输出如下
{t=TOM, j=JACK, l=LUCY}
使用toMap还有一点需要说明,就是key不能冲突,看下面代码,就会产生key冲突
Stream<String> namesStream = Stream.of("tom", "jack", "lucy","ttpfx");Map<Character, String> namesMap = namesStream.collect(Collectors.toMap(k -> k.charAt(0), v -> v.toUpperCase()));System.out.println(namesMap);
如果产生key冲突,那么collect方法会抛出一个一个IllegalStateException异常

对于key冲突的情况,我们应该给出解决key冲突的逻辑,toMap还有一个重载的方法,用于解决key冲突。也就是保留新值还是旧值

我们遇到key冲突旧保存最新的值即可
Stream<String> namesStream = Stream.of("tom", "jack", "lucy", "ttpfx");Map<Character, String> namesMap = namesStream.collect(Collectors.toMap(k -> k.charAt(0),v -> v.toUpperCase(),(oldV, newV) -> newV));System.out.println(namesMap);
上面代码输出如下
{t=TTPFX, j=JACK, l=LUCY}
对于toMap,都有一个等价的toConcurrentMap
关于流的一些说明(终结操作)
我们先来看下面代码
Stream<Integer> stream = Stream.of(2, 3, 1, 4);stream.forEach(System.out::println);Stream<Integer> newStream = stream.filter(x -> x > 2);newStream.forEach(System.out::println);
上面代码逻辑很简单,就是先输出流里面的元素,然后过滤一下,最后再输出。按理说这个代码应该是没有问题的,我们运行一下

可以发现报错了,报错的原因就是说流已经关闭了,很奇怪啊,我们明明没有执行close操作
造成流关闭的原因就是 forEach方法。还记得在文章开始的说明吗?流是惰性执行的,在流执行终止操作前,流其实都没有执行。而forEach就是一个终止操作。对于终结方法,我们可以简单理解为就是返回值不是Stream的方法。
我们用代码验证一下Stream的惰性执行
List<Integer> list = new ArrayList<>();list.add(1);Stream<Integer> stream = list.stream();list.add(2);long count = stream.count();System.out.println(count);
大家想一下,count是多少?由于Stream是惰性执行的,那么count显然应该就是2

总结
在这篇文章中介绍了Stream的一些基本使用,对于Stream还有许多的方法没有说明,这些会在后面的文章中进行说明。Stream里面还有一个很重要的Optional,这个将在下一篇文章中进行说明。
相关文章:
【java基础】Stream流的各种操作
文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法flatMap方法limit和skip方法distinct方法sorted方法收集结果收集为数组(toArray)收集为集合(collect)收集为Map关于流的一些说明(终结操…...
【Python练习】序列结构
目录 一、实验目标 二、实验内容...
CDN加速缓存的定义与作用
一、CDN的含义CDN的全称是Content Delivery Network,即内容分发网络。CDN是在原有互联网的基础上再构建虚拟分发网络,利用部署在各地的边缘节点服务器,充分发挥其负载均衡、内容分发智能调度等功能,让用户能够就地拉取数据&#x…...
Java并发高频面试题
分享50道Java并发高频面试题。 线程池 线程池:一个管理线程的池子。 为什么平时都是使用线程池创建线程,直接new一个线程不好吗? 嗯,手动创建线程有两个缺点 不受控风险频繁创建开销大 为什么不受控? 系统资源有…...
CVPR 2023 | 旷视研究院入选论文亮点解读
近日,CVPR 2023 论文接收结果出炉。近年来,CVPR 的投稿数量持续增加,今年收到有效投稿 9155 篇,和 CVPR 2022 相比增加 12%,创历史新高。最终,大会收录论文 2360 篇,接收率为 25.78 %。本次&…...
Vue3 学习总结补充(一)
文章目录1、Vue3中为什么修改变量的值后,视图不更新?2、使用 ref 还是 reactive?3、reactive 为什么会有响应性连接丢失情况?4、watch的不同使用方法5、watchEffect和 watch 的区别区别1:数据源的区别区别2:…...
使用ChatGPT 开放的 API 接口可以开发哪些自研工具?
使用ChatGPT开放的API接口,可以开发多种自研工具,例如: 智能聊天机器人:可以使用ChatGPT提供的语言生成能力,构建一个智能聊天机器人,能够根据用户的输入自动回复,完成自然语言交互。 文本生成工具:可以使用ChatGPT的文本生成能力,开发一个文本生成工具,例如自动生…...
I2C和SPI总线以及通信
通讯属性 概括 Serial/parallel 串行/并行Synchronous/asynchronous 同步/异步Point-to-point / bus 点对点 总线Half-duplex/full-duplex 半双工/全双工Master-slave/ equal partners 主从/对等single-ending / differential 单端/差分 点对点和总线 点对点通讯 只有两个通…...
Spring八股文
Bean的生命周期 1.通过反射生成对象 2.填充Bean的属性 3.调用aware接口的invokeAwareMethod方法,对BeanName、BeanFactory、BeanClassLoader对象的属性设值 4.调用BeanPostProcessor的前置处理方法,其中使用较多的是ApplicationContextPostProcessor…...
20 k8sMetric 简介
一. Metric 简介metrics-server 可实现 Kubernetes 的 Resource Metrics API(metrics.k8s.io),通过此 API 可以查询 Pod 与 Node 的部分监控指标,Pod 的监控指标用于 HPA、VPA 与 kubectl top pods -n ns 命令,而 Node…...
面试问了解Linux内存管理吗?10张图给你安排的明明白白
linux内存管理,内存管理好像离我们很远,但这个知识点虽然冷门(估计很多人学完根本就没机会用上)但绝对是基础中的基础,这就像武侠中的内功修炼,学完之后看不到立竿见影的效果,但对你日后的开发工…...
【C++】内联函数inline
文章目录概念使用特性原理概念 C中内联函数的出现解决了C语言宏函数的不足,类似于宏展开,这种在函数调用处直接嵌入函数体的函数称为内联函数,又称内嵌函数或内置函数。 以inline修饰的函数叫做内联函数,编译时C编译器会在调用内…...
C++演讲比赛流程管理系统_黑马
任务 学校演讲比赛,12人,两轮,第一轮淘汰赛,第二轮决赛 选手编号 [ 10001 - 10012 ] 分组比赛 每组6人 10个评委 去除最高分 最低分,求平均分 为该轮成绩 每组淘汰后三名,前三名晋级决赛 决赛 前三名胜出 …...
谈谈低代码的安全问题,一文全给你解决喽
低代码是一种软件开发方法,通过使用图形化用户界面和可视化建模工具,以及自动生成代码的技术,使得开发人员可以更快速地构建和发布应用程序。 作为近些年软件开发市场热门之一,市面上也涌现了许多低代码产品,诸如简道云…...
[数据结构]二叉树OJ(leetcode)
目录 二叉树OJ(leetcode)训练习题:: 1.单值二叉树 2.检查两棵树是否相同 3.二叉树的前序遍历 4.另一棵树的子树 5.二叉树的构建及遍历 6.二叉树的销毁 7.判断二叉树是否是完全二叉树 二叉树OJ(leetcode)训练习题:: 1.单值二叉…...
flutter 输入时插入分隔符
每四位插入一个分隔符import package:flutter/services.dart;class DividerInputFormatter extends TextInputFormatter {final int rear; //第一个分割位数,后面分割位,,数final String pattern; //分割符DividerInputFormatter({this.rear 4, this.pattern });overrideTex…...
静态版通讯录——“C”
各位CSDN的uu你们好呀,之前小雅兰学过了一些结构体、枚举、联合的知识,现在,小雅兰把这些知识实践一下,那么,就让我们进入通讯录的世界吧 实现一个通讯录: 可以存放100个人的信息每个人的信息:名…...
前端基础开发环境搭建工具等
一、基本开发环境(软件)安装1、Vscode(代码编辑器)官网下载网址:https://code.visualstudio.com/2、nvm(node多版本管理器,每个node版本都有对应的npm版本)安装包下载地址࿱…...
华为OD机试题【IPv4 地址转换成整数】用 Java 解 | 含解题说明
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典本篇题目:IPv4 地址转换成整数 题目 存在…...
[数据结构]排序算法
目录 常用排序算法的实现:: 1.排序的概念及其运用 2.插入排序 3.希尔排序 4.选择排序 5.冒泡排序 6.堆排序 7.快速排序 8.归并排序 9.排序算法复杂度及稳定性分析 10.排序选择题练习 常用排序算法的实现:: 1.排序的概念及其运用…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果