Java 8 中的 Stream API,用于处理集合数据

Java 8 引入了 Stream API,使得处理集合数据变得更加简洁和高效。Stream API 允许开发者以声明式编程风格操作数据集合,而不是使用传统的迭代和条件语句。

一、基本概念

1.1 什么是 Stream

Stream 是 Java 8 中的一个新抽象,它允许对集合数据执行各种复杂的操作,例如过滤、映射、规约、收集等。Stream 不存储数据,而是从集合或其他数据源(如数组、I/O channel 等)中获取数据并进行操作。

Stream 的主要特点包括:

  • 无存储:Stream 不存储数据,只是对数据进行操作。
  • 函数式编程:使用 lambda 表达式进行操作,使代码更简洁。
  • 延迟执行:Stream 操作是懒加载的,只有在需要结果时才会执行。
  • 可组合性:多个 Stream 操作可以连成一串操作链,形成一系列的转换。

1.2 Stream 的生命周期

Stream 的操作可以分为三类:

  • :创建 Stream 的数据源,例如集合、数组或 I/O channel。
  • 中间操作:返回新的 Stream 的操作,例如过滤、映射。
  • 终端操作:产生结果或副作用的操作,例如收集、计算。

一个 Stream 的生命周期可以简单描述为:

  1. 创建 Stream。
  2. 中间操作。
  3. 终端操作。

二、Stream API 的基本操作

2.1 创建 Stream

Stream 可以通过以下几种方式创建:

  • 从集合
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
  • 从数组
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
  • 从值
Stream<String> stream = Stream.of("a", "b", "c");
  • 从文件
Stream<String> stream = Files.lines(Paths.get("path/to/file.txt"));

2.2 中间操作

中间操作返回一个新的 Stream,它们是延迟执行的,只有在终端操作执行时才会实际进行计算。常用的中间操作包括:

2.2.1 filter

filter 用于对 Stream 中的元素进行过滤,只保留满足条件的元素。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);
2.2.2 map

map 用于将 Stream 中的每个元素映射到另一个元素。

List<String> words = Arrays.asList("Java", "Stream", "API");
Stream<Integer> wordLengths = words.stream().map(String::length);
2.2.3 flatMap

flatMap 用于将 Stream 中的每个元素映射到一个新的 Stream,并将这些新 Stream 合并成一个 Stream。

List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("c", "d"));
Stream<String> flatStream = listOfLists.stream().flatMap(Collection::stream);
2.2.4 distinct

distinct 用于去除 Stream 中的重复元素。

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
Stream<Integer> distinctNumbers = numbers.stream().distinct();
2.2.5 sorted

sorted 用于对 Stream 中的元素进行排序,可以传递一个比较器。

List<String> words = Arrays.asList("Java", "Stream", "API");
Stream<String> sortedWords = words.stream().sorted();

2.3 终端操作

终端操作会触发 Stream 的计算,并生成结果或副作用。常用的终端操作包括:

2.3.1 forEach

forEach 用于对 Stream 中的每个元素执行一个动作。

List<String> words = Arrays.asList("Java", "Stream", "API");
words.stream().forEach(System.out::println);
2.3.2 toArray

toArray 用于将 Stream 中的元素收集到一个数组中。

List<String> words = Arrays.asList("Java", "Stream", "API");
String[] array = words.stream().toArray(String[]::new);
2.3.3 reduce

reduce 用于将 Stream 中的元素通过一个关联函数组合起来,生成一个值。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().reduce(0, Integer::sum);
2.3.4 collect

collect 用于将 Stream 中的元素收集到一个容器中,例如 List、Set 或 Map。

List<String> words = Arrays.asList("Java", "Stream", "API");
List<String> upperCaseWords = words.stream().map(String::toUpperCase).collect(Collectors.toList());
2.3.5 count

count 用于返回 Stream 中的元素数量。

List<String> words = Arrays.asList("Java", "Stream", "API");
long count = words.stream().count();
2.3.6 findFirstfindAny

findFirst 用于返回 Stream 中的第一个元素(如果存在)。

List<String> words = Arrays.asList("Java", "Stream", "API");
Optional<String> first = words.stream().findFirst();

findAny 用于返回 Stream 中的任意一个元素(如果存在),常用于并行流。

List<String> words = Arrays.asList("Java", "Stream", "API");
Optional<String> any = words.stream().findAny();
2.3.7 anyMatchallMatchnoneMatch

这三个操作用于检查 Stream 中是否有任意、所有或没有元素满足指定的条件。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0);
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);

三、并行流

Java 8 提供了并行流,可以充分利用多核处理器的优势。只需调用 parallelStream 方法即可创建一个并行流。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().reduce(0, Integer::sum);

并行流通过将数据分成多个子流,并在不同的 CPU 核心上并行处理这些子流,然后再合并结果,来提高处理速度。需要注意的是,并行流适合于无状态和无副作用的操作,使用时需小心处理共享变量和同步问题。

四、Stream API 的最佳实践

4.1 使用 Lambda 表达式

Stream API 通常与 lambda 表达式一起使用,使代码更加简洁和易读。例如:

List<String> words = Arrays.asList("Java", "Stream", "API");
List<String> upperCaseWords = words.stream().map(word -> word.toUpperCase()).collect(Collectors.toList());

4.2 避免使用修改状态的中间操作

Stream 操作应该是无副作用的,即不应修改外部状态。以下示例展示了一个错误的用法:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> results = new ArrayList<>();
numbers.stream().forEach(n -> results.add(n * 2));  // 这样做是错误的

正确的做法是使用终端操作 collect

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> results = numbers.stream().map(n -> n * 2).collect(Collectors.toList());

4.3 利用方法引用

方法引用可以使代码更加简洁。例如,使用方法引用替代 lambda 表达式:

List<String> words = Arrays.asList("Java", "Stream", "API");
List<String> upperCaseWords = words.stream().map(String::toUpperCase).collect(Collectors.toList());

4.4 避免使用并行流进行小任务

并行流在处理大量数据或复杂计算时非常高效,但对于小任务,启动并行计算的开销可能会大于收益。因此,在数据量较小或计算较简单的情况下,优先使用顺序流。

4.5 避免在终端操作之前调用 findAny

在终端操作之前调用 findAny 会导致流的中间操作链被截断,进而无法正确执行后续的操作。例如:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = numbers.stream().filter(n -> n % 2 == 0).findAny(); // 这样做会中断流

应将 findAny 用作终端操作:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = numbers.stream().filter(n -> n % 2 == 0).findAny();

4.6 使用 collect 进行结果收集

collect 是一个强大的终端操作,可以将流中的元素收集到各种容器中。例如,收集到 List:

List<String> words = Arrays.asList("Java", "Stream", "API");
List<String> wordList = words.stream().collect(Collectors.toList());

4.7 使用 Collectors 进行复杂收集操作

Collectors 提供了多种收集器,可以进行复杂的结果收集。例如,收集到 Map:

List<String> words = Arrays.asList("Java", "Stream", "API");
Map<Integer, List<String>> wordLengthMap = words.stream().collect(Collectors.groupingBy(String::length));

4.8 使用 Optional 处理可能的空值

Stream API 中的某些终端操作会返回 Optional,例如 findFirstfindAny。使用 Optional 可以避免空指针异常:

List<String> words = Arrays.asList("Java", "Stream", "API");
Optional<String> firstWord = words.stream().findFirst();
firstWord.ifPresent(System.out::println);

Java 8 的 Stream API 为集合数据的处理提供了一种高效、简洁的方式。通过理解和掌握 Stream 的基本概念、常用操作以及最佳实践,可以大大提高 Java 开发的生产力和代码质量。

Stream API 不仅支持顺序流,还支持并行流,使得在多核环境下处理大量数据变得更加高效。在实际开发中,合理使用 Stream API 可以显著提升代码的可读性和稳定性。

黑马程序员免费预约咨询

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

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

相关文章

工厂生产计划难以执行的真正原因及对策

在制造业中&#xff0c;生产计划的执行对于企业的运营至关重要。然而&#xff0c;许多工厂在生产计划执行过程中面临着诸多挑战&#xff0c;尤其是物料齐套率低的问题。本文将探讨工厂生产计划难以执行的真正原因&#xff0c;并提出相应的解决对策。 一、生产计划难以执行的真…

mysql optimizer_switch : 查询优化器优化策略深入解析

码到三十五 &#xff1a; 个人主页 在 MySQL 数据库中&#xff0c;查询优化器是一个至关重要的组件&#xff0c;它负责确定执行 SQL 查询的最有效方法。为了提供DBA和开发者更多的灵活性和控制权&#xff0c;MySQL 引入了 optimizer_switch 系统变量。这个强大的工具允许用户开…

nginx配置WebSocket参数wss连接

目录 一、原文连接 二、 配置参数 三、实践 四、重启nginx 五、连接websocket 一、原文连接 nginx配置websocket支持wss-腾讯云开发者社区-腾讯云 二、 配置参数 map $http_upgrade $connection_upgrade { default upgrade; close; } upstream websocket { se…

【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)

目录 一、 进程1.1 PID(进程标识符)1.2 内存指针1.3 文件描述符表1.4 状态1.5 优先级1.6 记账信息1.7 上下文 二、线程三、总结&#xff1a;进程和线程之间的区别&#xff08;非常非常非常重要&#xff0c;面试必考题&#xff09; 一、 进程 简单来介绍一下什么是进程&#xf…

写入文件内容

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在实例01中&#xff0c;虽然创建并打开一个文件&#xff0c;但是该文件中并没有任何内容&#xff0c;它的大小是0KB。Python的文件对象提供了write()…

【电路笔记】-分贝

分贝 分贝是以 10 为底的对数比,用于表示电路中功率、电压或电流的增加或减少。 1、概述 一般来说,分贝是响度的度量。 在设计或使用放大器和滤波器电路时,计算中使用的一些数字可能非常大或非常小。 例如,如果我们将两个放大器级级联在一起,功率或电压增益分别为 20 和…

os和os.path模块

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 目录也称文件夹&#xff0c;用于分层保存文件。通过目录可以分门别类地存放文件。我们也可以通过目录快速找到想要的文件。在Python中&#xff0c;并…

033.搜索旋转排序数组

题意 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给方法之前&#xff0c;nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了旋转&#xff0c;使数组变为 [nums[k], nums[k1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]&…

古字画3d立体在线数字展览馆更高效便捷

在数字时代的浪潮中&#xff0c;大连图书馆以崭新的面貌跃然屏幕之上——3D全景图书馆。这座承载着城市文化精髓与丰富知识资源的数字图书馆&#xff0c;利用前沿的三维建模技术&#xff0c;为我们呈现了一个全新的知识世界。 随时随地&#xff0c;无论您身处何地&#xff0c;只…

信息学奥赛初赛天天练-22-C++基础关键字、进制转换、结构体与联合体的实用技巧大揭秘

PDF文档公众号回复关键字:20240607 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 1 在C中&#xff0c;下面哪个关键字用于声明一个变量&#xff0c;其值不能被修改&#xff1f;&#xff08; &#…

【Java】解决Java报错:StackOverflowError

文章目录 引言1. 错误详解2. 常见的出错场景2.1 无限递归2.2 递归深度过大2.3 方法调用层次过深 3. 解决方案3.1 优化递归算法3.2 尾递归优化3.3 增加调用栈大小3.4 检查递归终止条件 4. 预防措施4.1 使用迭代替代递归4.2 尾递归优化4.3 合理设计递归算法4.4 调整JVM参数4.5 定…

b端系统类管理平台设计前端开发案例

b端系统类管理平台设计前端开发案例

二叉树-堆的详解

一&#xff0c;树的概念 1&#xff0c;树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有…

vue3 + echarts 二次开发百分比饼图

效果图&#xff1a; 安装 pnpm i echarts 公共模块组件 <divclass"pie"ref"percent"style"width: 100%; height: calc(100% - 48px)"></div> import { ref, onMounted } from vue import * as echarts from echarts const prop…

【乐吾乐3D可视化组态编辑器】状态告警示例

状态告警的设置方法为两种&#xff1a; 1.通过数据点号设置&#xff08;推荐&#xff09;&#xff1a; 适用于绑定单一数据点号&#xff0c;设置逻辑简洁&#xff0c;实现简单逻辑交互 2.通过交互事件监听数据点号设置&#xff1a; 适用于绑定多个数据点号&#xff0c;实现复…

LLM大模型AI应用的三阶技术

第一阶 指令工程&#xff08;Prompt Enginner&#xff09; 设计提示&#xff08;Prompt Design&#xff09; 结果优化&#xff08;Response Optimization&#xff09; 交互设计&#xff08;Interaction Design&#xff09; 模型理解&#xff08;Model Understanding&#…

哈希经典题目(C++)

文章目录 前言一、两数之和1.题目解析2.算法原理3.代码编写 二、判定是否互为字符重排1.题目解析2.算法原理3.代码编写 三、 字⺟异位词分组1.题目解析2.算法原理3.代码编写 总结 前言 哈希表是一个存储数据的容器&#xff0c;我们如果想要快速查找某个元素&#xff0c;就可以…

MMUNet:形态学特征增强网络在结肠癌病理图像分割中的应用

MMUNet: Morphological feature enhancement network for colon cancer segmentation in pathological images. 发表在&#xff1a;Biomedical Signal Processing and Control2024--影响因子&#xff1a;3.137 南华大学的论文 论文地址&#xff1a;main.pdf (sciencedirecta…

Wakeup Source框架设计与实现

Wakeup Source 为系统组件提供了投票机制&#xff0c;以便低功耗子系统判断当前是否可以进入休眠。 Wakeup Source(后简称&#xff1a;WS) 模块可与内核中的其他模块或者上层服务交互&#xff0c;并最终体现在对睡眠锁的控制上。 1. 模块功能说明 WS的处理逻辑基本上是围绕 com…

后端进阶-分库分表

文章目录 为什么需要分库为什么需要分表 什么时候需要分库分表只需要分库只需要分表 分库分表解决方案垂直分库水平分库垂直分表水平分表 分库分表常用算法范围算法hash分片查表分片 分库分表模式客户端模式代理模式 今天跟着训练营学习了分库分表&#xff0c;整理了学习笔记。…