Java基础 - Stream 流:Stream API的中间操作

在上一篇博客中,我介绍了构建 Stream 流的多种方式,以及 Stream 流的特点和优势。如果你还没有阅读,你可以点击这里查看。

Java基础 - Stream 流:构建流的多种方式

在这篇博客中,我将探索 Stream API 的中间操作,它们可以让你对 Stream 流进行各种转换和过滤,从而得到你想要的结果。

Stream API 的中间操作是指那些返回一个新的 Stream 流对象的操作,它们不会消耗 Stream 流,也不会产生最终的结果,而是可以链式地调用,形成一个操作管道。Stream API 提供了很多中间操作,可以分为以下几类:

  • 筛选和切片:这类操作可以让你从 Stream 流中选择或者排除一些元素,例如 filter, distinct, limit, skip 等。
  • 映射:这类操作可以让你将 Stream 流中的每个元素转换为另一种类型或者形式,例如 map, flatMap, peek 等。
  • 排序:这类操作可以让你对 Stream 流中的元素进行排序,例如 sorted, reverseOrder 等。
  • 搜索:这类操作可以让你在 Stream 流中查找某些元素或者条件,例如 findAny, findFirst, anyMatch, allMatch, noneMatch 等。
  • 截断:这类操作可以让你在 Stream 流中截取某些元素或者条件,例如 takeWhile, dropWhile 等。

下面,我将用一些示例来展示这些中间操作的用法和效果。

1. 筛选和切片

1.1 filter

filter 操作可以让你根据一个谓词(Predicate)来筛选出 Stream 流中符合条件的元素,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用 filter 操作筛选出偶数
Stream<Integer> evenStream = numberStream.filter(n -> n % 2 == 0);// 输出 [2, 4, 6, 8, 10]
evenStream.forEach(System.out::println);

1.2 distinct

distinct 操作可以让你去除 Stream 流中的重复元素,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "cat", "elephant", "dog", "fox");// 使用 distinct 操作去除重复元素
Stream<String> uniqueStream = animalStream.distinct();// 输出 [cat, dog, elephant, fox]
uniqueStream.forEach(System.out::println);

1.3 limit

limit 操作可以让你限制 Stream 流中的元素个数,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> fruitStream = Stream.of("apple", "banana", "cherry", "durian", "elderberry", "fig");// 使用 limit 操作限制元素个数为 3
Stream<String> limitedStream = fruitStream.limit(3);// 输出 [apple, banana, cherry]
limitedStream.forEach(System.out::println);

1.4 skip

skip 操作可以让你跳过 Stream 流中的前 n 个元素,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> colorStream = Stream.of("red", "green", "blue", "yellow", "pink", "purple");// 使用 skip 操作跳过前 2 个元素
Stream<String> skippedStream = colorStream.skip(2);// 输出 [blue, yellow, pink, purple]
skippedStream.forEach(System.out::println);

2. 映射

2.1 map

map 操作可以让你将 Stream 流中的每个元素映射为另一种类型或者形式,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> nameStream = Stream.of("Alice", "Bob", "Charlie", "David");// 使用 map 操作将每个元素转换为大写
Stream<String> upperStream = nameStream.map(String::toUpperCase);// 输出 [ALICE, BOB, CHARLIE, DAVID]
upperStream.forEach(System.out::println);

2.2 flatMap

flatMap 操作可以让你将 Stream 流中的每个元素映射为另一个 Stream 流对象,然后将这些 Stream 流对象合并为一个 Stream 流对象,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> sentenceStream = Stream.of("Hello world", "Java 8", "Stream API");// 使用 flatMap 操作将每个元素分割为单词,并合并为一个 Stream 流对象
Stream<String> wordStream = sentenceStream.flatMap(s -> Stream.of(s.split(" ")));// 输出 [Hello, world, Java, 8, Stream, API]
wordStream.forEach(System.out::println);

2.3 peek

peek 操作可以让你在 Stream 流中的每个元素上执行一个消费者(Consumer)操作,同时返回一个新的 Stream 流对象,它和原来的 Stream 流对象包含相同的元素。这个操作通常用于调试或者观察 Stream 流的中间状态。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);// 使用 peek 操作在每个元素上打印一个消息
Stream<Integer> peekedStream = numberStream.peek(n -> System.out.println("Processing " + n));// 输出 [1, 2, 3, 4, 5]
peekedStream.forEach(System.out::println);

3. 排序

3.1 sorted

sorted 操作可以让你对 Stream 流中的元素进行自然排序或者指定排序规则,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 sorted 操作对元素进行自然排序
Stream<String> sortedStream = animalStream.sorted();// 输出 [cat, dog, elephant, fox, giraffe]
sortedStream.forEach(System.out::println);// 使用 sorted 操作对元素进行指定排序规则,按照长度逆序
Stream<String> reversedStream = animalStream.sorted((s1, s2) -> s2.length() - s1.length());// 输出 [elephant, giraffe, cat, dog, fox]
reversedStream.forEach(System.out::println);

3.2 reverseOrder

reverseOrder 操作可以让你对 Stream 流中的元素进行逆序排序,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);// 使用 reverseOrder 操作对元素进行逆序排序
Stream<Integer> reversedStream = numberStream.reverseOrder();// 输出 [5, 4, 3, 2, 1]
reversedStream.forEach(System.out::println);

4. 搜索

4.1 findAny

findAny 操作可以让你从 Stream 流中找到任意一个元素,返回一个 Optional 对象,它可能包含一个值,也可能为空。这个操作通常用于并行的 Stream 流,因为它不保证返回第一个元素。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 findAny 操作找到任意一个元素
Optional<String> anyAnimal = animalStream.findAny();// 输出 Optional[cat] 或者其他值
System.out.println(anyAnimal);

4.2 findFirst

findFirst 操作可以让你从 Stream 流中找到第一个元素,返回一个 Optional 对象,它可能包含一个值,也可能为空。这个操作通常用于串行的 Stream 流,因为它保证返回第一个元素。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 findFirst 操作找到第一个元素
Optional<String> firstAnimal = animalStream.findFirst();// 输出 Optional[cat]
System.out.println(firstAnimal);

4.3 anyMatch

anyMatch 操作可以让你判断 Stream 流中是否有任意一个元素满足一个谓词(Predicate),返回一个布尔值。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 anyMatch 操作判断是否有以 f 开头的元素
boolean hasF = animalStream.anyMatch(s -> s.startsWith("f"));// 输出 true
System.out.println(hasF);

4.4 allMatch

allMatch 操作可以让你判断 Stream 流中是否所有的元素都满足一个谓词(Predicate),返回一个布尔值。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 allMatch 操作判断是否所有的元素都包含 a
boolean allA = animalStream.allMatch(s -> s.contains("a"));// 输出 false
System.out.println(allA);

4.5 noneMatch

noneMatch 操作可以让你判断 Stream 流中是否没有任何一个元素满足一个谓词(Predicate),返回一个布尔值。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 noneMatch 操作判断是否没有以 z 结尾的元素
boolean noZ = animalStream.noneMatch(s -> s.endsWith("z"));// 输出 true
System.out.println(noZ);

5. 截断

5.1 takeWhile

takeWhile 操作可以让你从 Stream 流中截取满足一个谓词(Predicate)的元素,直到遇到第一个不满足的元素为止,返回一个新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用 takeWhile 操作截取小于 5 的元素
Stream<Integer> takenStream = numberStream.takeWhile(n -> n < 5);// 输出 [1, 2, 3, 4]
takenStream.forEach(System.out::println);

5.2 dropWhile

dropWhile 操作可以让你从 Stream 流中丢弃满足一个谓词(Predicate)的元素,直到遇到第一个不满足的元素为止,然后返回剩余的元素组成的新的 Stream 流对象。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用 dropWhile 操作丢弃小于 5 的元素
Stream<Integer> droppedStream = numberStream.dropWhile(n -> n < 5);// 输出 [5, 6, 7, 8, 9, 10]
droppedStream.forEach(System.out::println);

6. 注意事项

6.1 只能被消费一次

一个Steam只能被消费(执行终端操作)一次。如果重复进行消费则会抛出IllegalStateException

// 创建一个 Stream 流对象
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 正常执行
long count = wordStream.count();// 会抛出IllegalStateException
long anotherCount = wordStream.count();

6.2 并行流谨慎使用

Stream API 提供了并行流的支持,可以通过 parallel() 方法将顺序流转换为并行流。并行流是指可以利用多核处理器的优势,将一个流的元素分成多个数据块,然后用不同的线程并行地处理每个数据块的流。使用并行流可以提高性能,但也有一些注意事项。

  • 并行流不一定总是比顺序流快:并行流的性能受到很多因素的影响,比如数据源的可分割性,流的操作的计算成本,流的大小,硬件环境等。并行流会引入额外的开销,比如线程的分配和切换,数据的拆分和合并等。因此,并行流不适合处理小量的数据,或者涉及装箱拆箱,依赖前一个元素,有状态的操作等。在使用并行流之前,最好先进行性能测试,比较并行流和顺序流的效果,然后选择合适的方式。
  • 并行流需要考虑线程安全问题:并行流会使用默认的 ForkJoinPool 线程池来执行任务,这个线程池的大小默认是 CPU 的核心数,也可以通过设置系统属性 java.util.concurrent.ForkJoinPool.common.parallelism 来改变。如果并行流的操作涉及到共享变量的修改,或者调用了其他线程不安全的方法,就可能会导致数据的不一致或者错误。因此,在使用并行流时,要尽量避免产生副作用,或者使用线程安全的数据结构和方法,或者使用同步机制来保证线程安全。
  • 并行流可能会改变结果的顺序或者内容:并行流会将数据分成多个块,然后由不同的线程处理,这可能会导致结果的顺序和数据源的顺序不一致,或者结果的内容和数据源的内容不一致。例如,如果使用 findAny 操作在并行流中查找任意一个元素,可能会得到不同的结果,因为并行流不保证返回第一个元素。或者,如果使用 forEach 操作在并行流中遍历元素,可能会得到乱序的结果,因为并行流不保证按照数据源的顺序执行。因此,在使用并行流时,要注意选择合适的操作,或者使用 sorted 或者 forEachOrdered 等操作来保证结果的顺序。
// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用顺序流计算所有元素的长度之和
int sum1 = animalStream.mapToInt(String::length).sum();
System.out.println(sum1); // 输出 23// 使用并行流计算所有元素的长度之和
int sum2 = animalStream.parallel().mapToInt(String::length).sum();
System.out.println(sum2); // 输出 23,和顺序流相同,因为 sum 是一个无状态的终端操作// 使用顺序流查找任意一个以 f 开头的元素
Optional<String> any1 = animalStream.filter(s -> s.startsWith("f")).findAny();
System.out.println(any1); // 输出 Optional[fox]// 使用并行流查找任意一个以 f 开头的元素
Optional<String> any2 = animalStream.parallel().filter(s -> s.startsWith("f")).findAny();
System.out.println(any2); // 输出 Optional[fox] 或者 Optional[fox, giraffe],因为并行流不保证返回第一个元素// 使用顺序流遍历所有元素
animalStream.forEach(System.out::println); 
// 输出 cat, dog, elephant, fox, giraffe,和数据源的顺序相同// 使用并行流遍历所有元素
animalStream.parallel().forEach(System.out::println); 
// 可能输出 fox, dog, cat, elephant, giraffe,和数据源的顺序不同,因为并行流不保证按照数据源的顺序执行

6.3 惰性求值

Stream API 中间操作是惰性求值的,也就是说,它们不会立即执行,而是等到遇到一个终端操作时,才会触发整个操作管道的执行。这样可以提高性能,避免不必要的计算。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5);// 使用 filter 和 map 中间操作,但不会立即执行
Stream<Integer> transformedStream = numberStream.filter(n -> n % 2 == 0).map(n -> n * n);// 使用 forEach 终端操作,触发中间操作的执行
transformedStream.forEach(System.out::println); // 输出 4, 16

6.4 无状态和有状态

Stream API 中间操作可以分为无状态和有状态两种,无状态的操作是指不依赖于任何状态,只和当前元素有关的操作,例如 filter, map, peek 等;有状态的操作是指依赖于某些状态,需要考虑整个 Stream 流的元素的操作,例如 distinct, sorted, limit, skip 等。无状态的操作通常比有状态的操作更高效,因为它们不需要维护额外的状态,也不需要等待所有的元素都处理完毕。有状态的操作可能会影响并行性能,因为它们需要同步状态,或者等待所有的元素都到达。例如:

// 创建一个 Stream 流对象
Stream<String> animalStream = Stream.of("cat", "dog", "elephant", "fox", "giraffe");// 使用 filter 和 map 无状态操作,可以并行执行
Stream<String> upperStream = animalStream.parallel().filter(s -> s.length() == 3).map(String::toUpperCase);// 使用 distinct 和 sorted 有状态操作,需要同步状态或者等待所有元素
Stream<String> uniqueStream = animalStream.parallel().distinct().sorted();

6.5 短路和非短路

Stream API 中间操作可以分为短路和非短路两种,短路的操作是指不需要处理所有的元素,只要满足某些条件就可以停止的操作,例如 limit, takeWhile, dropWhile 等;非短路的操作是指需要处理所有的元素,不能提前停止的操作,例如 filter, map, sorted 等。短路的操作通常比非短路的操作更高效,因为它们可以减少不必要的计算,也可以和一些短路的终端操作配合,例如 anyMatch, findFirst, findAny 等。非短路的操作可能会影响性能,因为它们需要处理所有的元素,也不能和一些短路的终端操作配合。例如:

// 创建一个 Stream 流对象
Stream<Integer> numberStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 使用 limit 和 takeWhile 短路操作,可以提前停止
Stream<Integer> takenStream = numberStream.limit(5).takeWhile(n -> n < 4);// 使用 filter 和 map 非短路操作,需要处理所有元素
Stream<Integer> filteredStream = numberStream.filter(n -> n % 2 == 0).map(n -> n * n);

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

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

相关文章

动态规划(算法竞赛、蓝桥杯)--分组背包DP

1、B站视频链接&#xff1a;E16 背包DP 分组背包_哔哩哔哩_bilibili #include <bits/stdc.h> using namespace std; const int N110; int v[N][N],w[N][N],s[N]; // v[i,j]:第i组第j个物品的体积 s[i]:第i组物品的个数 int f[N][N]; // f[i,j]:前i组物品&#xff0c;能放…

学习JavaEE的日子 Day21 枚举

Day21 1.枚举的引入 需求&#xff1a;编写季节类&#xff08;Season&#xff09;&#xff0c;该类只有四个对象&#xff08;spring&#xff0c;summer&#xff0c;autumn&#xff0c;winter&#xff09; 概念&#xff1a;枚举&#xff08;enum&#xff09;全称为 enumeration&…

基带信号处理设计原理图:2-基于6U VPX的双TMS320C6678+Xilinx FPGA K7 XC7K420T的图像信号处理板

基于6U VPX的双TMS320C6678Xilinx FPGA K7 XC7K420T的图像信号处理板 综合图像处理硬件平台包括图像信号处理板2块&#xff0c;视频处理板1块&#xff0c;主控板1块&#xff0c;电源板1块&#xff0c;VPX背板1块。 一、板卡概述 图像信号处理板包括2片TI 多核DSP处理…

Linux进程管理:(二)进程调度原语

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 进程调度的概念比较简单&#xff0c…

Java学习笔记NO.17

T1&#xff1a;合并两个排序好的整数数组 import java.util.Arrays;public class MergeSortedArrays {public static int[] mergeArrays(int[] arr1, int[] arr2) {int[] result new int[arr1.length arr2.length];int i 0, j 0, k 0;while (i < arr1.length &&am…

一个简单的iOS天气应用程序源码

创建一个简单的iOS天气应用程序涉及到多个步骤&#xff0c;包括设置项目、编写代码和使用外部API。由于篇幅限制&#xff0c;我将提供一个基础的示例&#xff0c;这个例子会展示如何创建一个简单的UI&#xff0c;获取用户的当前位置&#xff0c;并从OpenWeatherMap API获取天气…

QPS 提升 10 倍!滴滴借助 StarRocks 物化视图实现低成本精确去重

作者&#xff1a;滴滴 OLAP 开发工程师 刘雨飞 小编导读&#xff1a; 滴滴于 2022 年引入了 StarRocks。经过一年多的努力&#xff0c;StarRocks 逐渐替代了原有技术栈&#xff0c;成为滴滴内部主要的 OLAP 引擎。截至 2023 年 12 月&#xff0c;滴滴已经成功建立了超过 40 个 …

Cesium插件系列——3dtiles压平

本系列为自己基于cesium写的一套插件具体实现。 这里是根据Cesium提供的CustomShader来实现的。 在CustomShader的vertexShaderText里&#xff0c;需要定义vertexMain函数&#xff0c;例如下&#xff1a; struct VertexInput {Attributes attributes;FeatureIds featureIds;…

LVGL常用部件使用总结之图片部件

图片部件可用于显示图片&#xff0c;图片源可以是 C 语言数组格式的文件、二进制的.bin 文件以及图标字体。值得注意的是&#xff0c;图片部件要显示 BMP、JPEG 等格式的图片&#xff0c;则必须经过解码。 图片部件的组成部分仅有一个&#xff1a;主体&#xff08;LV_PART_MAIN…

URI到底是个啥

URI是统一资源标识符&#xff08;Uniform Resource Identifier&#xff09;&#xff0c;URL是统一资源定位符&#xff08;Uniform Resource Locator&#xff09;。 具体如何标记和区分服务器上的资源用的其实就是URI&#xff0c;因为其经常出现在浏览器的地址栏里&#xff0c;…

Verilog(未完待续)

Verilog教程 这个教程写的很好&#xff0c;可以多看看。本篇还没整理完。 一、Verilog简介 什么是FPGA&#xff1f;一种可通过编程来修改其逻辑功能的数字集成电路&#xff08;芯片&#xff09; 与单片机的区别&#xff1f;对单片机编程并不改变其地电路的内部结构&#xff0…

Parallel Computing - 一文讲懂并行计算

目录 Throughput/LatencySerial ComputingParallel ComputingTypes of parallel computersSimple 4-width SIMDAmdahls lawTypes of parallelism**Data Parallel Model**Task parallel PartitioningDomain DecompositionFunctional Decomposition CommunicationsExample that d…

java调用chatgpt接口,实现专属于自己的人工智能助手

文章目录 前言导包基本说明请求参数响应参数创建请求和响应的VO类 代码编写使用最后说明 前言 今天突然突发奇想&#xff0c;就想要用java来调用chatget的接口&#xff0c;实现自己的聊天机器人&#xff0c;但是网上找文章&#xff0c;属实是少的可怜(可能是不让发吧)。找到了…

ESP32 web 对接华为云平台--MQTT协议

文章目录 前言一、MQTT协议二、如何使用MQTT协议对接华为云1.注册华为云账号2.设备接入中创建资源空间3.如何连接4.通过MQTT.fx工具做初步对接4.1 设置连接信息4.2 连接平台 5.查看平台设备信息 三. 设备测对接平台1.ESP测引入MQTT库2.编码2.1前端编码修改2.2 后端接口修改 3.M…

element-plus+vue3表单含图片(可预览)(线上图片)

一、要实现的效果&#xff1a; 二、如果期间出现这样的效果&#xff08;表格穿透过来了&#xff09;&#xff0c;加上了这行代码就可以了&#xff1a; preview-teleported“true” 如果仅测试用&#xff0c;建议使用线上图片链接的形式&#xff0c;免得本地地址不生效&#xf…

SSH 的两种认证方式

SSH&#xff08;Secure Shell&#xff09;提供了几种不同的认证方式&#xff0c;其中两种最常见的方式是密码认证和密钥认证。以下是它们的详细介绍&#xff1a; 密码认证&#xff1a; 原理&#xff1a;用户在连接时输入用户名和密码&#xff0c;然后将密码传输到远程服务器进行…

数学实验-Matlab使用(1)

使用方法以及笔记均在文件中 class1_func1.m function f class1_func1(x) % f为输出&#xff0c;输出有多个时需要用中括号以矩阵的方式包起来 % x为输入f sin(x)class1_func2.m function [a,b,u,v] class1_func2(x,y)[a,b] eig(x)[u,v] eig(y)class1.m % 当语句后有…

yolov9从头开始训练

yolov9从头开始训练 一、准备数据集 数据集相关文件存放布局如下 yolov9-datasets ├── train │ ├── images │ │ ├── image.jpg │ │ ├── │ └── labels │ ├── image.txt │ ├── ├── valid │ ├── images │ │ ├── image.jpg │ │ ├─…

吴恩达deeplearning.ai:模型选择交叉验证测试集的训练方法

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 在上一节中&#xff0c;我们了解了如何利用测试集来评估模型性能。今天我们来进一步完善这个想法&#xff0c;让你使用该技术自动选择一个更好的模型。 文章目录 模型选择交叉验证 Cross Va…

SpringBoot 框架(上)

SpringBoot SpringBoot概述依赖管理自动配置SpringBoot 注解使用ConfigurationImport(value {Cat.class,Dog.class})ImportResource(locations "classpath:beans.xml") yaml 标记语言概述基本语法数据类型字面量对象数组 使用细节 Rest 风格请求处理概述注意事项 接…