Stream 流详细总结

Stream 流详细总结

    • 一、Stream 是什么
    • 二、流的创建
      • 1、Stream 创建
      • 2、Collection 集合创建(最常见的一种)
      • 3、Array 数组创建
      • 4、文件创建
      • 5、函数创建
    • 三、流的操作
      • 1、中间操作
        • distinct 去重
        • filter 过滤
        • map 映射
        • flatMap 映射汇总
        • sorted 排序
        • limit 截断
        • skip 跳过
        • peek 观察
      • 2、终止操作
        • match 断言
        • count 计数
        • collect 收集
            • ① toList
            • ② toMap
            • ③ toSet
            • ④ counting
            • ⑤ summingInt
            • ⑥ averagingInt
            • ⑦ joining
            • ⑧ maxBy、minBy
            • ⑨ groupingBy
        • forEach 遍历
        • findFirst 返回第一个元素
        • max、min 最大值、最小值
        • sum 求和
        • concat 组合
        • toXXX 转换
        • reduce 规约

一、Stream 是什么

Stream 是 Java 8 新增的重要特性,它提供函数式编程支持并允许以管道方式操作集合,流操作会遍历数据源,使用管道式操作处理数据后生成结果集合,这个过程通常不会对数据源造成影响。

同时 stream 不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组、Java 容器或 I/O channel 等。在 Stream 中的操作每一次都会产生新的流,内部不会像普通集合操作一样立刻获取值,而是惰性取值,只有等到用户真正需要结果的时候才会执行。

Stream 代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的。
在这里插入图片描述
流和集合的区别

  • 不存储数据:流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作
  • 函数式编程:流的操作不会修改数据源,例如 filter 不会将数据源中的数据删除
  • 延迟操作:流的很多操作如 filter、map 等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行
  • 可以解绑:对于无限数量的流,有些操作是可以在有限的时间完成的,比如 limit(n) 或 findFirst(),这些操作可以实现 “短路” (Short-circuiting),访问到有限的元素后就可以返回
  • 纯消费:流的元素只能访问一次,类似 lterator,操作没有回头路,如果你想从头重新访问流的元素,对不起,你得重新生成一个新的流

集合讲的是数据,流讲的是计算
在这里插入图片描述

二、流的创建

生成流的方式主要有五种

1、Stream 创建

	Stream<Integer> stream = Stream.of(1,2,3,4,5);

2、Collection 集合创建(最常见的一种)

	List<Integer> integerList = new ArrayList<>();
    integerList.add(1);
    integerList.add(2);
    integerList.add(3);
    integerList.add(4);
    integerList.add(5);
	Stream<Integer> listStream = integerList.stream();

3、Array 数组创建

	int[] intArr = {1, 2, 3, 4, 5};
    IntStream arrayStream = Arrays.stream(intArr);

通过 Array.stream 方法生成流,生成的是数值流 [IntStream] 而不是对象流 Stream
注:使用数值流可以避免计算过程中拆箱装箱,提高性能

Stream API 提供了 mapToInt、mapToDouble、mapToLong 三种方式将对象流 [Stream] 转换成对应的数值流,同时提供了 boxed 方法将数值流转换成对象流

4、文件创建

	try {
    	Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
    } catch (IOException e) {
    	e.printStackTrace();
    }

通过 Files.lines方法得到一个流,并且得到的每个流都是给定文件中的一行

5、函数创建

iterator

	Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(5);

iterator 方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为 iterator 生成的流为无限流,通过 limit 方法对流进行了截断,只生成 5 个偶数
generator

	Stream<Double> generateStream = Stream.generate(Math::random).limit(5);

generator 方法接受一个参数,方法参数类型为 Supplier,由它为流提供值。generator 生成的流也是无限流,因此通过 limit 方法对流进行截断

三、流的操作

流的操作类型主要分为两种:中间操作、终止操作

1、中间操作

distinct 去重

distinct 保证数据源中的重复元素在结果中只出现一次,它使用 equals() 方法判断两个元素是否相等

/**filter去重方法实现类**/
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}
	
	// 1.String 集合去重
	List<String> list_str = list.stream().distinct().collect(Collectors.toList());
	
	// 2.实体对象集合去重,判断依据是整个实体对象
	List<Student> list_student = list.stream(S).distinct().collect(Collectors.toList());
	
	// 3.根据 list 中某个属性去重
	List<Student> list_name1 = liststream().collect(collectingAndThen(
                               toCollection(() -> new 
                               TreeSet<(Comparator.comparing(Student::getName))),
                               ArrayList::new));

	List<Student> list_name2 = list.stream().filter(distinctByKey(Student::getName))
                                     .collect(Collectors.toList());

在第3中情况中,我们首先创建了一个方法作为 Stream.filter() 的参数,其返回类型为 Predicate,原理就是判断一个元素能否加入到 Set 中去,用到了 Set 集合的属性

filter 过滤

filter 对所有元素进行检查,只有断言函数为真的元素才会出现在结果中,不会对数据源进行修改

	// 输出不为空的字符
	List<String> list_notNull = list.stream().filter(ObjectUtil::isNotNull).toList();

	// 输出ID大于 6的 user对象
	List<User> filetr = userList.stream().filter(x-> x.getId() > 6).collect(Collectors.toList());
map 映射

map 根据传入的mapper函数对元素一对一映射,即数据源中的每一个元素都会在结果中被替换(映射)为mapper函数的返回值,也可以根据处理返回不同数据类型

	// 输出id
	List<Long> list_ids = list.stream().map(ProblemList::getId).toList();

	// 用指定字符连接流中的每个名称,并输出
	String str_name = list.stream().map(ProblemList::getName).collect(Collectors.joining(", "));

	// 输出新的IdName集合
	List<IdName> list_IdName = list.stream().map(
							x -> new IdName().setId(x.getId()).setName(x.getName())
							).toList();
flatMap 映射汇总

flatMap 将映射后的流的元素全部放入到一个新的流中

	List<IdName> list = new ArrayList<>();
    list.add(new IdName().setId(1L).setName("aa,bb,cc"));
    list.add(new IdName().setId(2L).setName("11,22,33"));
    list.add(new IdName().setId(3L).setName("ⅠⅠ,ⅡⅡ,ⅢⅢ"));
	
	// 数据拆分一对多映射
	list.stream().flatMap(x -> Arrays.stream(x.getName().split(","))).forEach(System.out::println);

在这里插入图片描述

sorted 排序

sorted 将流中的元素进行排序

	// 根据名字倒序
	List<User> list = userList.stream().sorted(Comparator.comparing(User::getName).reversed()).toList();
limit 截断

limit 返回指定数量的前n个元素的流

	// 获取前5条数据
	List<User> list_limit = list.stream().limit(5).toList();
skip 跳过

skip 返回丢弃了前n个元素的流,如果流中的元素小于或者等于n,则返回空的流

	// 跳过第3条取后面的数据
	List<User> list_skip = list.stream().skip(3).toList();
peek 观察

peek 主要作用是在流的每个元素上执行一个操作,比如打印值、记录日志、调试等。通常用于调试和观察流的中间状态,而不会对流的内容进行修改

	List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

	List<Integer> doubledNumbers = numbers.stream()
				    .peek(n -> System.out.println("Processing number: " + n))
				    .map(n -> n * 2)
				    .collect(Collectors.toList());

创建一个整数列表 numbers,然后通过流的方式对每个元素进行处理。在流的 peek 操作中,打印元素的值,然后我们使用 map 操作将每个数字乘以2,并将结果收集到一个新的列表中。
运行上面代码时,会看到以下输出:
在这里插入图片描述
需要注意的是,peek 方法是一个中间操作,它不会触发流的终端操作。如果你希望对流的内容进行修改或获取最终的结果,你需要在 peek 方法之后添加一个终止操作,比如 collect、forEach 等。

	//每个用户ID加1输出
	userList.stream().peek(user -> user.setId(user.getId()+1)).forEach(System.out::println);

2、终止操作

match 断言
  • allMatch :只有在所有元素都满足断言时才返回 true,否则返回 false,流为空时总是返回 true
  • anyMatch :只有在任意一个元素满足断言时返回 true,否则返回 false
  • noneMatch :只有在所有元素都不满足断言时才返回 true,否则返回 false
	// allMatch:检查是否匹配所有元素
	boolean matchAll = userList.stream().allMatch(user -> "北京".equals(user.getCity()));
	
	// anyMatch:检查是否至少匹配一个元素
	boolean matchAny = userList.stream().anyMatch(user -> "北京".equals(user.getCity()));
	
	// noneMatch:检查是否没有匹配所有元素,返回boolean
	boolean nonaMatch = userList.stream().allMatch(user -> "云南".equals(user.getCity()));
count 计数

count 返回流中的元素的数量

	long count = userList.stream().filter(user -> user.getAge() > 20).count();
collect 收集

collect 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 stream 中元素做汇总的方法。下面是一些常见的搜集器:

① toList

把流中元素收集到 List 集合中

	// 将用户ID存放到List集合中
	List<Integer> idList = userList.stream().map(User::getId).collect(Collectors.toList());
② toMap

把流中元素收集到 Map 集合中

	// 将 ID和 user以 Key-Value形式存放到 Map集合中
	Map<Long,User> userMap = list.stream().collect(Collectors.toMap(User::getId, x -> x, (a,b)->a));

注意,这里的 toMap方法中多了一个参数【(a,b) -> a】,表示如果遇到 Key相同的情况,Value保存新值(b)或旧值(a)

③ toSet

把流中元素收集到 Set 集合中

	// 将用户所在城市存放到Set集合中
	Set<String> citySet = userList.stream().map(User::getCity).collect(Collectors.toSet());
④ counting

计算流中元素的个数

	// 符合条件的用户总数
	long count = userList.stream().filter(user -> user.getId()>1).collect(Collectors.counting());
⑤ summingInt

对流中元素的整数属性求和

	// 计算ID大于2的用户ID之和
	Integer sumInt = userList.stream()
				.filter(user -> user.getId()>2)
				.collect(Collectors.summingInt(User::getId));
⑥ averagingInt

计算元素 Integer 属性的均值

	// 计算用户的平均年龄
	Double avg = userList.stream().collect(Collectors.averagingInt(Student::getAge));
⑦ joining

连接流中的每个字符串

	// 将用户所在城市,以指定分隔符链接成字符串
	String joinCity = userList.stream().map(User::getCity).collect(Collectors.joining("||"));
⑧ maxBy、minBy

根据比较器选择最大值、最小值

	// 筛选元素中年龄最大的用户
	User maxId = userList.stream().collect(Collectors.maxBy(Comparator.comparingLong(User::getAge))).get();

	// 筛选元素中ID最小的用户
	User maxId = userList.stream().collect(Collectors.minBy(Comparator.comparingLong(User::getId))).get();
⑨ groupingBy

根据某属性值对流分组,属性为K,结果为V

	// 以城市对用户进行分组
	Map<String,List<User>> groupCity = userList.stream().collect(Collectors.groupingBy(User::getCity));
forEach 遍历

forEach 遍历流的每一个元素,执行指定的 action。它是一个终点操作,但不保证按照流的 encounter order 顺序执行,对于有序流要按照顺序执行可使用 forEachOrdered 方法

	list.stream().forEach(System.out::println);
findFirst 返回第一个元素

findFirst 返回第一个元素,如果流为空,返回空的Optional

	User user = list.stream().findFirst().orElse(null);

orElse( null ):表示如果一个都没找到 返回 null;orElse() 中可以塞默认值,如果找不到就会返回默认值

max、min 最大值、最小值

max、min 返回流中的最大值、最小值

	Long max_id = list.stream().map(User::getId).max(Long::compare).orElse(0L);
	Integer min_age = list.stream().map(User::getAge).min(Comparator.comparing(x -> x)).orElse(null);
sum 求和

sum 求流的某属性之和

	// 求ID之和
	int sum = userList.stream().mapToInt(User::getId).sum();
concat 组合

concat 用来连接类型一样的两个流

	// 求ID之和
	List<Integer> list1 = Arrays.asList(1,2,3);
	List<Integer> list2 = Arrays.asList(4,3,2);
	Stream.concat(list1.stream(),list2.stream()).forEach(System.out::println);

在这里插入图片描述

toXXX 转换

toXXX toArray 将一个流转换成数组,如果想转换成其他集合类型,也可调用 collect 方法,利用 Collectors.toXXX方法进行转换

	List<User> toList = userList.stream().toList();
	Integer[] integers = Stream.of(1, 2, 3, 4, 5).toArray(Integer[]::new);
reduce 规约

reduce 把stream中的元素组合起来
它需要我们首先提供一个起始种子,然后依照某种运算规则使其与stream的第一个元素发生关系产生一个新的种子,这个新的种子再紧接着与stream的第二个元素发生关系产生又一个新的种子,就这样依次递归执行,最后产生的结果就是reduce的最终产出

	// 没有起始值,返回为 Optional类型
	Optional<Long> reduce = list.stream().map(ProblemList::getId).reduce(Long::sum); // 结果:15
 	
 	// 给一个起始值 1
 	Long reduce_1 = list.stream().map(ProblemList::getId).reduce(1L, Long::sum); // 结果:16

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LTE之接口协议

一、接口协议栈 接口是指不同网元之间的信息交互方式。既然是信息交互&#xff0c;就应该使用彼此都能看懂的语言&#xff0c;这就是接口协议。接口协议的架构称为协议栈。根据接口所处位置分为空中接口和地面接口&#xff0c;响应的协议也分为空中接口协议和地面接口协议。空…

mysql复习笔记06(小滴课堂)

mysql数据安全之备份的背景意义 介绍数据备份 mysql数据安全之mysqldump备份实例&#xff08;跨机器&#xff09; 一般存在于mysql的bin目录下。中小型企业&#xff0c;数据量不是特别大的时候可以使用这个方式备份。 可以看到备份过来的库了。这是备份单个数据库。 如果想备份…

龙芯loongarch64服务器编译安装tensorflow-io-gcs-filesystem

前言 安装TensorFlow的时候,会出现有些包找不到的情况,直接使用pip命令也无法安装,比如tensorflow-io-gcs-filesystem,安装的时候就会报错: 这个包需要自行编译,官方介绍有限,这里我讲解下 编译 准备 拉取源码:https://github.com/tensorflow/io.git 文章中…

论文写作工具 - 基于Tkinter的AI模型与文档处理

本工具不开源&#xff0c;需要的联系chsengni163.com 论文写作工具 - 基于Tkinter的AI模型与文档处理 概述 这个工具是一个基于Python的Tkinter库创建的图形用户界面应用&#xff0c;旨在帮助用户利用AI模型编写论文并自定义文档格式。通过结合AI技术和文档处理功能&#xf…

java八股 设计模式

企业场景篇-03-设计模式-工厂设计模式-工厂方法模式_哔哩哔哩_bilibili 1.简单工厂模式 新加咖啡类的时候需要在唯一的那个工厂类里加代码&#xff0c;这样就耦合了 2.工厂模式 相对于简单模式的一个工厂生产所有咖啡&#xff0c;这里只定义了一个抽象咖啡工厂&#xff0c;然…

【数据结构】最短路径算法实现(Dijkstra(迪克斯特拉),FloydWarshall(弗洛伊德) )

文章目录 前言一、Dijkstra&#xff08;迪克斯特拉&#xff09;1.方法&#xff1a;2.代码实现 二、FloydWarshall&#xff08;弗洛伊德&#xff09;1.方法2.代码实现 完整源码 前言 最短路径问题&#xff1a;从在带权有向图G中的某一顶点出发&#xff0c;找出一条通往另一顶点…

FPGA设计时序约束十三、Set_Data_Check

目录 一、序言 二、Set Data Check 2.1 基本概念 2.2 设置界面 2.3 命令语法 三、工程示例 3.1 工程代码 3.2 约束设置 3.3 时序报告 四、参考资料 一、序言 通常进行时序分析时&#xff0c;会考虑触发器上时钟信号与数据信号到达的先后关系&#xff0c;从而进行setu…

文字编辑软件,批量给多个文本添加文档内容

在当今信息爆炸的时代&#xff0c;文字编辑工作是很多人需要面对的&#xff0c;而怎么快速的完成编辑工作&#xff0c;则是很多人所思考解决的。现在有一款很好用的软件——首助编辑高手&#xff0c;可以批量对多个文本文档内容进行处理&#xff0c;能帮你在文字编辑的工作上节…

开关电源厚膜集成电路引脚功能

开关电源厚膜集成电路引脚功能 一、 STR51213、STR50213、STR50103 引脚号 引脚功能 1 接地&#xff0c;内接稳压基准电路 2 开关管基极 3 开关管集电极 4 开关管发射极 5 误差比较电压信号输入&#xff0c;兼待机控制 二、 STR3302、STR3202 引脚号 引脚功能 1内部半…

融资项目——swagger2接口分类配置

在一般开发中&#xff0c;各种Controller可能会被分为两种&#xff1a;后台管理员的相关Controller与用户的相关Controller。所以在使用swagger2的时候&#xff0c;我们也希望其分为两个大类。其解决方法如下&#xff1a; Configuration EnableSwagger2 public class Swagger2…

基于docker-compose 安装Sonar并集成gitlab

文章目录 1. 前置条件2. 编写docker-compose-sonar.yml文件3. 集成 gitlab4. Sonar Login with GitLab 1. 前置条件 安装docker-compose 安装docker 创建容器运行的特有网络 创建挂载目录 2. 编写docker-compose-sonar.yml文件 version: "3" services:sonar-postgre…

DFS与BFS算法总结

知识概览 DFS、BFS都可以对整个问题空间进行搜索&#xff0c;搜索的结构都是像一棵树。DFS会尽可能往深搜&#xff0c;当搜索到叶节点时就会回溯。而BFS每一次只会扩展一层。 DFS与BFS的区别&#xff1a; 搜索方式数据结构空间复杂度性质DFS栈O(h)&#xff0c;其中h为搜索空间…

Epson打印机连接wifi

环境 Epson L3153 打印机联通无线光猫 背景 最近家里的联通宽带不太稳定&#xff0c;经常断网。今天打了联通客服电话&#xff0c;师傅上门来&#xff0c;说可能是光猫用的时间太长了&#xff0c;换了一个新的联通光猫&#xff0c;问题解决。 wifi的名称是 CU_Y3ft 和 CU_Y3…

ARM 点灯

.text .global _start _start: led1设置GPIOE时钟使能 RCC_MP_AHB4ENSETR[4]->1 0X50000A28LDR R0,0X50000A28 指定寄存器地址LDR R1,[R0] 将寄存器数值取出来放在R1中ORR R1,R1,#(0x1<<4) 将第4位设置为1STR R1,[R0] 将修改后的值写回去设置PE10为输出 GPIOE…

RocketMQ事务消息实现分布式事务

文章目录 简介实现原理实现逻辑 简介 RocketMQ事务消息 RocketMQ在4.3.0版中支持分布式事务消息&#xff0c;这里RocketMQ的事务消息是采用2PC(两段式协议) 补偿机制&#xff08;消息回查&#xff09;的分布式事务功能。提供消息发送与业务落库的一致性。 RocketMQ事务消息&am…

强化学习(五)-Deterministic Policy Gradient (DPG) 算法及公式推导

针对连续动作空间&#xff0c;策略函数没法预测出每个动作选择的概率。因此使用确定性策略梯度方法。 0 概览 1 actor输出确定动作2 模型目标&#xff1a; actor目标&#xff1a;使critic值最大 critic目标&#xff1a; 使TD error最大3 改进&#xff1a; 使用两个target 网络…

Redis缓存数据一致性

实际业务中常使用Redis缓存来提升读写效率&#xff0c;减少存储层的压力。因为数据在缓存和DB中各存储一份&#xff0c;所以会出现数据一致性的问题。总体来说导致数据不一致的原因主要有两个。请求并发和操作非原子。 请求并发是指同时可能有多个读写请求同时请求Cache或者DB&…

【C++】bind绑定包装器全解(代码演示,例题演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

非线性约束的优化问题_序列二次规划算法代码

1. 理论部分 2. 序列二次规划算法代码及解析 3.完整代码 1.理论部分 a.约束优化问题的极值条件 库恩塔克条件(Kuhn-Tucker conditions&#xff0c;KT条件)是确定某点为极值点的必要条件。如果所讨论的规划是凸规划&#xff0c;那么库恩-塔克条件也是充分条件。 &#xff…

5.OpenResty系列之深入理解(一)

本文基于Centos8进行实践&#xff0c;请读者自行安装OpenResty。 1. 内部调用 进入默认安装路径 cd /usr/local/openresty/nginx/conf vim nginx.conflocation /sum {# 只允许内部调用internal;content_by_lua_block {local args ngx.req.get_uri_args()ngx.print(tonumber…