【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语句时会先从这里找一下,…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
React父子组件通信:Props怎么用?如何从父组件向子组件传递数据?
系列回顾: 在上一篇《React核心概念:State是什么?》中,我们学习了如何使用useState让一个组件拥有自己的内部数据(State),并通过一个计数器案例,实现了组件的自我更新。这很棒&#…...
break 语句和 continue 语句
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行 break break语句用于跳出代码块或循环 1 2 3 4 5 6 for (var i 0; i < 5; i) { if (i 3){ break; } console.log(i); } continue continue语句用于立即终…...
SQLSERVER-DB操作记录
在SQL Server中,将查询结果放入一张新表可以通过几种方法实现。 方法1:使用SELECT INTO语句 SELECT INTO 语句可以直接将查询结果作为一个新表创建出来。这个新表的结构(包括列名和数据类型)将与查询结果匹配。 SELECT * INTO 新…...
