【JDK1.8 新特性】Stream API
1. 前言
- Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
- Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库
最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。 - Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
2. 什么是Stream?为什么需要Stream?
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,讲的是存储数据,而 Stream 是有关计算的(排序、查找、过滤、映射、遍历等),讲的是对数据进行计算。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
实际开发中,项目中多数数据源都来自于MySQL、Oracle等,这些都是使用SQL区操作。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理,而Stream提供了一套功能强大的处理数据的API。
注意:
① Stream 自己不会存储元素。
② Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
④ Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。
3. Stream的操作三个步骤
-
创建 Stream
一个数据源(如:集合、数组),获取一个流 -
中间操作
每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象。因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。 -
终止操作(终端操作)
终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。

3.1 创建Stream
方式一:通过集合创建Stream流
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
- default Stream stream() : 返回一个顺序流
- default Stream parallelStream() : 返回一个并行流
并行流 是指将一个大的数据集合分成多个小的数据块,分配给多个线程并行处理的流。并行流可以提高数据处理的速度,特别是在处理大数据集合的时候。Java 8使用Fork/Join框架来实现并行流。
使用并行流的方式:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 创建并行流
Stream<Integer> parallelStream = list.parallelStream();
// 对并行流进行操作
parallelStream.forEach(System.out::println);
顺序流 是指将数据集合按照一定的顺序依次处理的流。顺序流是默认的流类型,它可以保证数据的处理顺序和原始数据集合的顺序一致。
使用顺序流的方式:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 创建顺序流
Stream<Integer> stream = list.stream();
// 对顺序流进行操作
stream.forEach(System.out::println);
区别:
- 并行流能够自动将数据集合分成多个小的数据块并行处理,而顺序流是按照顺序依次处理数据集合。
- 并行流可以提高数据处理的速度,特别是在处理大数据集合的时候,而顺序流适合处理小数据集合。
- 并行流需要消耗额外的计算资源,比如CPU和内存等,而顺序流不需要额外的计算资源。
方式二:通过数组创建Stream流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
- static Stream stream(T[] array): 返回一个流
- public static IntStream stream(int[] array):返回一个int类型的流
- public static LongStream stream(long[] array):返回一个Long类型的流
- public static DoubleStream stream(double[] array):返回一个Double类型的流
@Test
public void test02(){String[] arr = {"hello","world"};Stream<String> stream = Arrays.stream(arr); //返回一个流
}@Test
public void test03(){int[] arr = {1,2,3,4,5};IntStream stream = Arrays.stream(arr);//返回一个int类型的流
}
方式四:将非集合类创建Stream流
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
- public static Stream of(T… values) : 返回一个流
@Test
public void test04(){Stream<Integer> stream = Stream.of(1,2,3,4,5);stream.forEach(System.out::println);
}
方式五:创建无限流(了解)
无限流是指没有固定大小的流,可以无限地生成数据。与之相对的是有限流,有限流有固定大小,只包含有限的数据元素。无限流可以使用Java 8中的Stream API来创建。
需要注意的是,无限流是无限的,因此在使用时需要注意控制流的大小,避免出现无限循环或者无限等待等情况。
创建无限流的方式主要有以下几种:
-
使用Stream.generate()方法创建无限流,该方法接受一个Supplier类型的参数,用于生成数据元素。
// 创建一个无限流,该流每次生成一个随机整数 Stream<Integer> infiniteStream = Stream.generate(() -> new Random().nextInt()); -
使用Stream.iterate()方法创建无限流,该方法接受一个起始值和一个UnaryOperator类型的参数,用于对起始值进行迭代操作。
// 创建一个无限流,该流每次迭代对上一次的结果进行平方操作 Stream<Integer> infiniteStream = Stream.iterate(1, n -> n * n); -
使用IntStream.range()或IntStream.rangeClosed()方法创建无限流,这两种方法用于生成一个区间范围内的整数流,可以用于构建无限流。
// 创建一个无限流,该流每次生成一个从0开始的整数 IntStream infiniteStream = IntStream.range(0, Integer.MAX_VALUE);
方式六:创建空Stream
Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。
//报NullPointerException
//Stream<Object> stream1 = Stream.of(null);
//System.out.println(stream1.count());//不报异常,允许通过
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());//3//不报异常,允许通过
List<String> list = new ArrayList<>();
list.add("AA");
list.add(null);
System.out.println(list.stream().count());//2
//ofNullable():允许值为null
Stream<Object> stream1 = Stream.ofNullable(null);
System.out.println(stream1.count());//0Stream<String> stream = Stream.ofNullable("hello world");
System.out.println(stream.count());//1
3.2 中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
1. 筛选与切片
filter(Predicatep):接收 Lambda , 从流中排除某些元素
distinct():筛选去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素,一定要重写 hashCode() 和 equals()
limit(long maxSize):截断流,使其元素不超过给定数量
skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
2. 映射
map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f): 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3. 排序
sorted():产生一个新流,其中按自然顺序排序
sorted(Comparatorcom):产生一个新流,其中按比较器顺序排序
4. 终止操作
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
- 流进行了终止操作后,不能再次使用。
1. 匹配与查找
allMatch(Predicate p):检查是否匹配所有元素
anyMatch(Predicate p) :检查是否至少匹配一个元素
noneMatch(Predicatep):检查是否没有匹配所有元素
findFirst():返回第一个元素
findAny():返回当前流中的任意元素
count():返回流中元素总数
max(Comparator c):返回流中最大值
min(Comparator c):返回流中最小值
forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
2. 归约
reduce(T identity, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回 Optional
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
3. 收集
collect(Collector c):将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
| 方法 | 返回类型 | 作用 |
|---|---|---|
| toList | Collector<T, ?, List> | 把流中元素收集到List |
| toSet | Collector<T, ?, Set> | 把流中元素收集到Set |
| toCollection | Collector<T, ?, C> | 把流中元素收集到创建的集合 |
| counting | Collector<T, ?, Long> | 计算流中元素的个数 |
| summingInt | Collector<T, ?, Integer> | 对流中元素的整数属性求和 |
| averagingInt | Collector<T, ?, Double> | 计算流中元素Integer属性的平均值 |
| summarizingInt | Collector<T, ?, IntSummaryStatistics> | 收集流中Integer属性的统计值。如:平均值 |
| joining | Collector<CharSequence, ?, String> | 连接流中每个字符串 |
| maxBy | Collector<T, ?, Optional> | 根据比较器选择最大值 |
| minBy | Collector<T, ?, Optional> | 根据比较器选择最小值 |
| reducing | Collector<T, ?, Optional> | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
| collectingAndThen | Collector<T,A,RR> | 包裹另一个收集器,对其结果转换函数 |
| groupingBy | Collector<T, ?, Map<K, List>> | 根据某属性值对流分组,属性为K,结果为V |
| partitioningBy | Collector<T, ?, Map<Boolean, List>> | 根据true或false进行分区 |
4. iterator重载
jdk 9.0新特性
//原来的控制终止方式:
Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println);//现在的终止方式:
Stream.iterate(1,i -> i < 100,i -> i + 1).forEach(System.out::println);
相关文章:
【JDK1.8 新特性】Stream API
1. 前言 Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力&…...
Springboot Maven打包跳过测试的五种方式总结 -Dmaven.test.skip=true
使用Maven打包的时候,可能会因为单元测试打包失败,这时候就需要跳过单元测试。也为了加快打包速度,也需要跳过单元测试。 Maven跳过单元测试五种方法。 在正式环境中运行Springboot应用,需要先打包,然后使用java -ja…...
静态链接和动态链接的区别
链接即为编译(包含预编译,编译和汇编过程)完成之后的过程,此过程又分为静态链接和动态链接两种方式。 1、静态链接 静态链接就是在生成可执行文件的时候(链接阶段),把所有需要的函数的二进制代…...
MATLAB学习笔记1
MATLAB学习笔记1 - 向量和矩阵 Matlab的数组可以是行向量,列向量,矩阵形式等 1.利用[ ]创建数组 例:包含7和9的一个数组,使用空格或,为行 x [7 9]//x是一个1*2的矩阵 y[7,9]//y是一个1*2的矩阵例:包含7和…...
Gorm -- 查询记录
文章目录查询单条记录通过结构体查询对应表指定表并将查询一条记录结果放至字典中按照主键查询查询多行记录按照主键查询使用结构体查询指定表名查询并放至字典列表中指定查询字段查询条件Where 条件(、like、in)通过结构体或字典设置查询条件或非排序Li…...
「Python 基础」错误、调试与测试
文章目录1. 错误处理2. debugassertloggingpdbIDE3. unittest编写运行setUp 与 tearDown4. doctest1. 错误处理 try:# 可能有异常的代码块r 10/int(2) except ValueError as e:# 有异常时执行,捕获指定类型及其子类型的错误print(ValueError, e) except ZeroDivis…...
17万字 JUC 看这一篇就够了(一) (精华)
JUC 今天我们来进入到 Java并发编程 JUC 框架的学习 ,内容比较多,但希望我们都能静下心来,耐心的看完这篇文章 文章目录JUC进程概述对比线程创建线程ThreadRunnableCallable线程方法APIrun startsleep yieldjoininterrupt打断线程打断 park终…...
C++右值引用/移动语义
在此之前,我们所用的引用,其实都是左值引用。 int a 10; int& ra a; 下面我们来重新认识一下引用: 而何为左值?左值引用其实是什么?请往下看~ 左值是一个表示数据的表达式(如变量名或解引用的指针)ÿ…...
小樽C++ 多章⑧ (叁) 指针与字符串、(肆) 函数与指针
目录 叁、函数与字符串 肆、函数与指针 4.1 指针作为函数参数 4.2 函数返回指针 4.3 函数指针与函数指针数组 4.4 结构体指针 小樽C 多章⑧ (壹) 指针变量https://blog.csdn.net/weixin_44775255/article/details/129031168 小樽C 多章⑧ …...
Mybatis-Plus
新建个项目 引入lombok devtools web mysql驱动 pom.xml引入mybatis-plus依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version> </dependency> sp…...
yolov8行人识别教程(2023年毕业设计+源码)
yolov8识别视频直接上YOLOv8的结构图吧,小伙伴们可以直接和YOLOv5进行对比,看看能找到或者猜到有什么不同的地方? Backbone:使用的依旧是CSP的思想,不过YOLOv5中的C3模块被替换成了C2f模块,实现了进一步的轻…...
CAD指令框找不到了怎么调出来?CAD指令框调出方法
CAD制图过程中,为了提高设计师的绘图效率,经常会用到各种CAD命令快捷键,可是CAD指令框突然不见了,这就让人很头疼了。CAD指令框找不到了怎么调出来呢?本节内容小编以浩辰CAD软件为例来给大家分享一下CAD指令框调出方法…...
一般用哪些工具做大数据可视化分析?
做数据分析这些年来,从刚开始的死磕excel,到现在成为数据分析行业的偷懒大户,使用过的工具还真不少! 这篇分享一些我在可视化工具上的使用心得,由简单到复杂,按照可视化类型一共分为纯统计图表类、GIS地图…...
Python每日一练(20230308)
目录 1. Excel表列名称 ★ 2. 同构字符串 ★★ 3. 分割回文串 II ★★★ 🌟 每日一练刷题专栏 C/C 每日一练 专栏 Python 每日一练 专栏 1. Excel表列名称 给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。 例如࿱…...
jvm之堆解读
堆(Heap)的核心概述 堆针对一个JVM进程来说是唯一的,也就是一个进程只有一个JVM,但是进程包含多个线程,他们是共享同一堆空间的。 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。 Java堆区…...
重构·改善既有代码的设计.02
前言之前在《重构改善既有代码的设计.01》中初步了解了重构的基本前提,基础原则等入门知识。今天我们继续第二更......识别代码的坏味道Duplicated Code 重复代码。最单纯的Duplicated Code就是“同一个类中含有相同的表达式”或“两个互为兄弟的子类内含有相同表达…...
脑电信号处理总成
目录一. EEG(脑电图)1.1 脑波1.2 伪迹1.2.1 眼动伪迹1.2.2 肌电伪迹1.2.3 运动伪迹1.2.4 心电伪迹1.2.5 血管波伪迹1.2.6 50Hz和静电干扰1.3 伪迹去除方法1.3.1 避免伪迹产生法1.3.2 直接移除法1.3.3 伪迹消除法一. EEG(脑电图) 1.1 脑波 脑波(英语:br…...
判断推理之图形推理
考点一动态位置变化(一)平移1.特征:图形在平面上的移动,图形本身的大小和形状不发生改变。2.方向:直线(上下、左右、斜对角线),绕圈(顺时针、逆时针)3.距离&a…...
【预告】ORACLE Unifier v22.12 虚拟机发布
引言 离ORACLE Primavera Unifier 最新系统 v22.12已过去了3个多月,应盆友需要,也为方便大家体验,我近日将构建最新的Unifier的虚拟环境,届时将分享给大家,最终可通过VMWare vsphere (esxi) / workstation 或Oracle …...
Sql执行流程与Redo log、 Undo log、 Bin log日志文件
文章目录Sql执行流程与日志文件Sql的执行流程Redo LogBin logUndo logSql执行流程与日志文件 Sql的执行流程 mysql的内部组件结构如下图所示 连接器 与客户端建立连接,检验登录密码,分配相应权限 查询缓存 执行sql语句时会先从这里找一下,…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...
