Java Stream流介绍及使用指南

背景

在Java 8之前,处理集合数据(如ListSetMap)通常意味着编写冗长的、以操作为中心的代码:创建迭代器、使用forwhile循环遍历元素、在循环体内进行条件判断和操作、收集结果。这种方式虽然有效,但不够简洁、可读性较差,且难以充分利用多核处理器的优势。

Java 8 引入的 Stream API (java.util.stream) 彻底改变了这一局面。 它提供了一种高效、声明式、函数式处理数据序列(尤其是集合)的强大抽象。Stream 不是数据结构,而是对数据源(如集合、数组、I/O资源)进行复杂计算操作的流水线。它允许你以声明式的方式(描述“做什么”,而不是“怎么做”)来表达数据处理逻辑,极大地提升了代码的简洁性、可读性和潜在的性能(尤其是并行处理)。

一、什么是 Stream?

  1. 非数据结构: Stream 本身不存储数据。它从数据源(如集合、数组)获取数据,并携带数据流经一系列计算操作。

  2. 函数式操作: Stream 的操作(如filtermapreduce) 通常接受函数式接口(如PredicateFunctionConsumer) 作为参数,这使其天然支持Lambda表达式和方法引用。

  3. 流水线: Stream 操作被链接起来形成一个流水线(Pipeline)。一个Stream操作的结果作为下一个操作的输入。

  4. 惰性求值: 中间操作(Intermediate Operations) 是惰性的。它们只是声明了要执行的操作,但不会立即执行。只有终止操作(Terminal Operation) 被调用时,整个流水线才会被触发执行。

  5. 只能遍历一次: 一个Stream实例一旦被终止操作消费,就不能再被使用。你需要从原始数据源重新创建一个新的Stream来执行其他操作。

  6. 支持并行: 创建并行Stream(parallelStream())非常简单,Stream API内部会自动处理线程和分区的复杂性(尽管使用时仍需注意线程安全)。

二、Stream 操作类型

Stream操作主要分为两类:

  1. 中间操作:

    • filter(Predicate<T> predicate): 过滤元素,保留满足条件的元素。

    • map(Function<T, R> mapper): 将元素转换成另一种形式(类型可以改变)。例如,从Student对象中提取name属性形成新的字符串流。

    • flatMap(Function<T, Stream<R>> mapper): 将每个元素转换成一个Stream,然后把所有生成的Stream“扁平化”连接成一个Stream。常用于处理嵌套集合(如List<List<String>>)。

    • distinct(): 去除重复元素(依据equals())。

    • sorted() / sorted(Comparator<T> comparator): 排序。

    • peek(Consumer<T> action): 对每个元素执行一个操作(通常用于调试,如打印),不影响元素本身。注意: 在并行流中慎用,执行顺序不确定。

    • limit(long maxSize): 截取前N个元素。

    • skip(long n): 跳过前N个元素。

    • 总是返回一个新的Stream,允许操作链式调用。

    • 惰性执行: 它们只是将操作记录到流水线上,直到终止操作被调用才真正执行。

  2. 终止操作:

    • forEach(Consumer<T> action): 对每个元素执行操作。

    • toArray(): 将元素收集到数组中。

    • 收集器 (Collectors): 最强大和灵活的终止操作之一。

    • reduce(...): 将元素反复结合,得到一个汇总值(如求和、求最大值)。有多个重载形式(带初始值、不带初始值、BinaryOperator)。

    • min(Comparator<T> comparator) / max(Comparator<T> comparator): 返回最小/最大元素(基于比较器)。

    • count(): 返回元素数量。

    • anyMatch(Predicate<T> predicate) / allMatch(Predicate<T> predicate) / noneMatch(Predicate<T> predicate): 检查是否存在任意/所有/没有元素匹配给定条件。这些是短路操作(找到结果即停止)。

    • findFirst() / findAny(): 返回第一个/任意一个元素(Optional<T>)。findAny()在并行流中效率更高。也是短路操作

    • collect(Collector<T, A, R> collector): 使用Collectors工具类提供的方法(如toList()toSet()toMap()joining()groupingBy()partitioningBy()summingInt()averagingDouble()等)将元素汇总成各种形式的结果(集合、字符串、数值统计等)。触发流水线的执行。

    • 消费Stream并产生一个结果(如一个值、一个集合、void)或副作用。执行后,Stream就被认为已消费,不能再使用。

三、为什么使用 Stream?核心优势

  1. 声明式编程: 代码更清晰、更接近问题描述。你关注的是“过滤出大于10的数”、“提取名称字段”、“按年龄分组”这样的逻辑,而不是循环索引和临时变量。

  2. 简洁性: 显著减少样板代码(如循环、迭代器、临时集合)。

  3. 可读性: 链式调用和Lambda表达式使数据处理逻辑一目了然。

  4. 易于并行化: 只需将stream()替换为parallelStream()(或在现有流上调用parallel()),即可尝试利用多核处理器加速计算。Stream API 内部处理了复杂的线程、同步和数据分区问题。(注意:并行不一定总是更快,需要评估数据量和操作复杂度)

  5. 函数式风格: 鼓励使用无副作用的纯函数,有利于编写更健壮、可测试的代码。

  6. 组合性: 中间操作可以灵活组合,构建复杂的数据处理流水线

四、代码列子

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamDemo {public static void main(String[] args) {List<String> names = Arrays.asList("abc", "java", "python", "David", "Anna", "Edward");// 示例1:过滤以"A"开头的名字,转换成大写,收集到ListList<String> aNamesUpper = names.stream() // 1. 创建流.filter(name -> name.startsWith("A")) // 2. 中间操作:过滤.map(String::toUpperCase) // 3. 中间操作:转换 (方法引用).collect(Collectors.toList()); // 4. 终止操作:收集// 示例2:计算所有名字长度的总和 (使用mapToInt避免自动拆箱装箱)int totalLength = names.stream().mapToInt(String::length) // 转换成IntStream (原始类型流).sum(); // IntStream的终止操作:求和System.out.println("Total Length: " + totalLength);// 示例3:按名字长度分组Map<Integer, List<String>> namesByLength = names.stream().collect(Collectors.groupingBy(String::length));System.out.println(namesByLength);// 示例4:并行流 - 查找任意一个长度大于5的名字 (顺序无关紧要时)names.parallelStream().filter(name -> name.length() > 5).findAny().ifPresent(System.out::println); }
}

五、关键注意事项

  1. 一次消费: Stream 只能被终止操作消费一次。之后再次使用会抛出IllegalStateException。需要从原始数据源重新创建。

  2. 避免有状态的Lambda: 在中间操作的Lambda表达式(特别是并行流中)应避免修改外部状态(如外部变量),否则可能导致线程安全问题或不可预测的结果。尽量使用无状态操作和纯函数。

  3. 并行流谨慎使用:

    • 开销: 并行化本身(线程创建、任务调度、结果合并)有开销。数据量小或操作简单时,串行流可能更快。

    • 线程安全: 确保数据源、共享状态、传递给操作的Lambda都是线程安全的。避免修改源集合(使用并发集合或确保只读)。

    • 顺序依赖性: 如果操作结果依赖于元素顺序(如findFirst()limit()sorted()在并行流中可能更慢),或者操作本身有状态(如skip()),并行可能无益甚至有害。

    • 副作用: 在forEachpeek中进行有副作用的操作(如修改共享集合)在并行流中极易出错。优先使用无副作用的collect进行汇总。

  4. peek用于调试: peek主要用于调试观察流水线中间状态,不应依赖它执行关键业务逻辑,尤其在并行流中其执行顺序不确定。

  5. 原始类型流: 为避免频繁的自动装箱(int -> Integer)带来的性能损耗,提供了IntStreamLongStreamDoubleStream。使用mapToIntmapToLongmapToDouble等方法转换,并使用其专用的方法(如sum()average()range())。

六、总结

Java Stream API 是现代Java编程中处理集合数据的基础。它通过声明式、函数式的风格,显著提升了代码的简洁性、可读性和潜在性能。理解其核心概念(流水线、惰性求值、中间/终止操作)、熟练掌握常用操作(filtermapcollectreduce等)以及Collectors工具类的强大功能,是高效利用Stream的关键。

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

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

相关文章

JDK 1.7 vs JDK 1.8

JDK版本比较 Java平台的两次重大飞跃&#xff1a;JDK 7的稳定优化与JDK 8的革命性创新引言&#xff1a;Java的进化之路Java作为企业级开发的支柱语言&#xff0c;其版本更新直接影响着全球数百万开发者。JDK 1.7&#xff08;2011年发布&#xff09;和JDK 1.8&#xff08;2014年…

张量与维度

3x4x5的张量&#xff1a; x torch.tensor([[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], [[21, 22, 23, 24, 25], …

智慧菜场系统(源码+文档+讲解+演示)

引言 在数字化浪潮的推动下&#xff0c;传统菜市场也在寻求创新与变革。智慧菜场系统作为一种新型的菜市场管理工具&#xff0c;通过数字化手段优化菜市场的全流程&#xff0c;提高运营效率&#xff0c;增强消费者体验&#xff0c;提升市场管理质量。本文将详细介绍智慧菜场系统…

【GESP】C++一级真题 luogu-B4355 [GESP202506 一级] 值日

GESP C一级&#xff0c;2025年6月真题&#xff0c;基础运算和循环语句&#xff0c;难度★☆☆☆☆。 题目题解详见&#xff1a;【GESP】C一级真题 luogu-B4355 [GESP202506 一级] 值日 | OneCoder 【GESP】C一级真题 luogu-B4355 [GESP202506 一级] 值日 | OneCoderGESP C一级…

【Linux应用】Ubuntu20.04 aarch64开发板一键安装ROS2(清华源)

【Linux应用】Ubuntu20.04 aarch64开发板一键安装ROS2&#xff08;清华源&#xff09; 文章目录相关资料更改UTF8执行更新一键安装ROS2验证配置环境变量附录&#xff1a;开发板快速上手&#xff1a;镜像烧录、串口shell、外设挂载、WiFi配置、SSH连接、文件交互&#xff08;RAD…

【HDLBits习题 2】Circuit - Sequential Logic(4)More Circuits

1. Rule90&#xff08;Rule 90&#xff09;方法1&#xff1a;module top_module (output reg [511:0] q,input clk,input load,input [511:0] data ); integer i;always (posedge clk) beginif (load 1b1) beginq < data;end else beginfor (i0; i<$bits(q);…

基于mysqlfrm工具解析mysql数据结构文件frm表结构和数据库版本信息

这里使用Linux系统上操作。win上搞了下 python报错。所以在这里记录一下推荐大家使用linux系统操作。 安装mysql utilswget https://downloads.mysql.com/archives/get/p/30/file/mysql-utilities-1.6.5.tar.gztar -xf mysql-utilities-1.6.5.tar.gzcd mysql-utilities-1.6.5py…

【C++ 深入解析 C++ 模板中的「依赖类型」】

深入解析 C 模板中的「依赖类型」 依赖类型是 C 模板编程中的核心概念&#xff0c;特指那些依赖于模板参数的类型。迭代器是依赖类型的常见例子&#xff0c;但远不止于此。让我们全面解析这个重要概念&#xff1a; 依赖类型的本质定义 依赖类型是&#xff1a; 在模板中定义直接…

Telnet远程连接实验(Cisco)

Telnet远程连接实验&#xff08;Cisco&#xff09; 拓扑图一并实现DHCP服务、HTTP服务、FTP服务。 二层交换机配置&#xff1a; 交换机Switch0配置&#xff1a; vlan 10vlan 20int f0/1switchport mode accessswitchport access vlan 10int f0/2switchport mode accessswitchpo…

C++:非类型模板参数,模板特化以及模板的分离编译

目录 一、前言 二、非类型模板参数 三、模板的特化 3.1 类模板特化 3.11 全特化 3.12 偏特化 3.2 函数模板特化 3.3 注意 四、模板的分离编译 一、前言 前面的文章梳理了模板初阶的一些用法&#xff0c;在后面梳理了STL的一些容器的用法后&#xff0c;下面将用到含有S…

【Qt 学习之路】Qt Android开发环境搭建:Ubuntu的Vmware虚拟机中的踩坑实录

文章目录1、简介2、虚拟机内USB设备识别难题2.1、正确连接手机2.2、打开USB相关配置2.3、打开虚拟机中的手机设备3、Gradle下载速度缓慢之困3.1、下载 Gradle 镜像3.2、安放镜像位置3.3、修改项目中的gradle路径1、简介 许久未曾使用Qt进行Android开发&#xff0c;今日在Ubunt…

MySQL中使用group_concat遇到的问题及解决

在使用group_concat的过程中遇到个问题&#xff0c;这里记录一下&#xff1a;在MySQL中有个配置参数group_concat_max_len&#xff0c;它会限制使用group_concat返回的最大字符串长度&#xff0c;默认是1024。 查询group_concat_max_len大小&#xff1a; show variables like…

高性能小型爬虫语言与代码示例

高性能小型爬虫现在有哪几种新兴语言可以选择。我看到了很多关于爬虫框架的信息&#xff0c;特别是使用Go语言和Node.js的框架。Go语言方面有Kaola1和Katana2这两个框架。Kaola被描述为高性能的Go语言爬虫框架&#xff0c;轻量级且强大&#xff0c;提供灵活配置选项。 Node.js…

【PTA数据结构 | C语言版】在顺序表 list 中查找元素 x

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将 n 个整数存入顺序表&#xff0c;对任一给定整数 x&#xff0c;查找其在顺序表中的位置。 输入格式&#xff1a; 输入首先在第一行给出正整数 n&#xff08;≤10^4 &#…

claude code-- 基于Claude 4 模型的智能编程工具,重塑你的编程体验

文章目录0.前言1.安装nodejs2.使用指南3.快速上手4.总结0.前言 最近的这个claudecode非常的火&#xff0c;因为可能是这个cursoe定价的一些原因吧&#xff0c;我是听其他的这个大佬说的&#xff0c;因为这个cursor其实我就是最开始的使用用过一下&#xff0c;现在基本上不使用…

HTTP API 身份认证

互联网系统通常需要根据用户身份决定是否有资源的访问权限&#xff0c;这就需要对用户进行身份认证&#xff08;Authentication&#xff09;&#xff0c;验证用户所声称的身份。验证手段通常是验证只有用户知道或拥有的东西&#xff0c;比如密码、手机号、指纹等。 基于浏览器…

Python毕业设计232—基于python+Django+vue的图书管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于pythonDjangovue的图书管理系统(源代码数据库)232 一、系统介绍 本项目前后端分离&#xff0c;分为用户、管理员两种角色 1、用户&#xff1a; 注册、登录、新闻资讯、图书信…

Koa+Puppeteer爬虫教程页面设计

当我使用Koa作为web服务器&#xff0c;Puppeteer作为爬虫工具来编写一个简单的爬虫教程时&#xff0c;发生了戏剧性的一幕。 下面我将创建一个完整的Koa Puppeteer爬虫教程页面&#xff0c;包含代码示例、执行演示和详细说明。设计思路 左侧为教程内容区域右侧为实时爬虫演示区…

云成本优化完整指南:从理论到实践的全方位解决方案

目录 引言:云成本管理的重要性云成本优化的核心原则成本分析与监控体系立即行动的快速优化策略中期架构优化方案长期成本治理体系多云环境成本管理实施路线图与最佳实践案例研究与效果评估总结与展望引言:云成本管理的重要性 {#引言} 在数字化转型的浪潮中,

计算机学科专业基础综合(408)四门核心课程的知识点总结

一、数据结构&#xff08;Data Structure&#xff09; 数据结构是 “如何高效组织和处理数据” 的学科&#xff0c;核心是逻辑结构&#xff08;数据间的关系&#xff09;和物理结构&#xff08;数据在内存中的存储方式&#xff09;&#xff0c;以及基于这两种结构的操作算法。 …