Java 8 Stream 流操作全解析
文章目录
- **一、Stream 流简介**
- **二、Stream 流核心操作**
- **1. 创建 Stream**
- **2. 中间操作(Intermediate Operations)**
- **filter(Predicate<T>):过滤数据**
- **1. 简单条件过滤**
- **2. 多条件组合**
- **3. 过滤对象集合**
- **4. 过滤 `null` 值**
- **2. map(Function<T, R>):转换元素**
- **一、`map` 的核心特性**
- **2. 提取对象属性**
- **3. 复杂转换逻辑**
- **3. flatMap(Function<T, Stream<R>>):扁平化嵌套结构**
- **4. distinct():去重**
- **5. sorted():排序**
- **6. limit(n) 和 skip(n):分页控制**
- **三、高级用法**
- **1. 分组与分区**
- **2. 并行流处理**
- **3. 原始类型流**
- **四、实际应用场景示例**
- **1. 数据转换与过滤**
- **2. 统计与汇总**
- **3. 复杂集合处理**
- **4. 分组统计**
- **五、注意事项与最佳实践**
- **六、与传统循环的对比**
一、Stream 流简介
Java 8 引入的 Stream
提供了一种高效、声明式处理集合数据的方式,支持顺序和并行操作,核心特点包括:
- 链式调用:通过组合中间操作(如
filter
,map
)和终端操作(如collect
,forEach
)实现复杂逻辑。 - 延迟执行:只有终端操作触发时才会执行中间操作。
- 不可重用:每个流只能被消费一次。
二、Stream 流核心操作
1. 创建 Stream
-
集合创建:
Collection.stream()
或parallelStream()
。List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream();
-
数组创建:
Arrays.stream(array)
。String[] arr = {"a", "b", "c"}; Stream<String> stream = Arrays.stream(arr);
-
静态方法:
Stream.of()
或生成无限流Stream.iterate()
,Stream.generate()
。Stream<Integer> numbers = Stream.of(1, 2, 3); Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2); // 0, 2, 4, ...
2. 中间操作(Intermediate Operations)
filter(Predicate):过滤数据
作用:根据条件筛选元素,保留满足条件的元素。
示例:
List<String> filtered = list.stream().filter(s -> s.startsWith("a")) // 保留以"a"开头的字符串.collect(Collectors.toList());
最佳实践:
1. 简单条件过滤
筛选出符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);// 筛选所有偶数
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList()); // [2, 4, 6]
2. 多条件组合
使用逻辑运算符 &&
(与)、||
(或)组合条件。
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");// 筛选长度大于5 且 以字母a开头的单词
List<String> result = words.stream().filter(s -> s.length() > 5 && s.startsWith("b")).collect(Collectors.toList()); // ["banana"]
3. 过滤对象集合
根据对象属性筛选数据。
class User {String name;int age;// 构造方法、getter/setter 省略
}List<User> users = Arrays.asList(new User("Alice", 25),new User("Bob", 17),new User("Charlie", 30)
);// 筛选年龄 >= 18 的用户
List<User> adults = users.stream().filter(user -> user.getAge() >= 18).collect(Collectors.toList()); // [Alice, Charlie]
4. 过滤 null
值
使用 Objects::nonNull
过滤掉 null
元素。
List<String> listWithNulls = Arrays.asList("a", null, "b", null, "c");// 过滤掉所有 null 值
List<String> nonNullList = listWithNulls.stream().filter(Objects::nonNull).collect(Collectors.toList()); // ["a", "b", "c"]
2. map(Function<T, R>):转换元素
map
是 Java Stream 中最核心的中间操作之一,用于将流中的元素一对一转换为另一种形式。它的本质是通过一个函数(Function<T, R>
)对每个元素进行映射,生成新的元素流。以下是 map
的深入解析,涵盖使用场景、底层机制、最佳实践与常见问题。
一、map
的核心特性
- 一对一转换:每个输入元素对应一个输出元素,元素数量不变。
- 类型转换:输入类型
T
可转换为任意输出类型R
(如String
→Integer
)。 - 惰性求值:只有终端操作触发时才会执行映射逻辑。
- 无副作用:理想情况下,映射函数不修改外部状态(符合函数式编程原则)。
作用:将元素转换为另一种类型或提取特定属性。
1. 简单类型转换
// 将字符串转换为大写
List<String> upperCaseList = Arrays.asList("apple", "banana", "cherry").stream().map(String::toUpperCase).collect(Collectors.toList()); // ["APPLE", "BANANA", "CHERRY"]
2. 提取对象属性
// 从User对象中提取name属性
List<String> names = users.stream().map(User::getName).collect(Collectors.toList());
3. 复杂转换逻辑
// 将字符串转换为自定义DTO对象
List<DataDTO> dtos = strings.stream().map(s -> {DataDTO dto = new DataDTO();dto.setValue(s.length());dto.setLabel(s.toUpperCase());return dto;}).collect(Collectors.toList());
3. flatMap(Function<T, Stream>):扁平化嵌套结构
作用:将嵌套集合(如 List<List<T>>
)展开为单一流。
示例:
List<List<String>> nestedList = Arrays.asList(Arrays.asList("a", "b"),Arrays.asList("c", "d")
);
List<String> flatList = nestedList.stream().flatMap(Collection::stream) // 将每个List<String>转换为Stream<String>.collect(Collectors.toList()); // ["a", "b", "c", "d"]
典型场景:
- 处理数据库查询的多表关联结果。
- 合并多个API响应的数据列表。
4. distinct():去重
作用:基于 equals()
和 hashCode()
去重。
示例:
List<Integer> unique = numbers.stream().distinct().collect(Collectors.toList()); // [1, 2, 3]
关键点:
-
自定义对象去重:需重写
equals()
和hashCode()
。class User {private Long id;@Overridepublic boolean equals(Object o) { /* 基于id比较 */ }@Overridepublic int hashCode() { /* 基于id生成 */ } }
-
性能注意:对大数据集去重可能消耗内存,可结合
limit
分批次处理。
5. sorted():排序
作用:按自然顺序或自定义比较器排序。
示例:
List<String> sorted = list.stream().sorted(Comparator.reverseOrder()) // 逆序排序.collect(Collectors.toList());
优化建议:
- 尽早过滤:先
filter
减少待排序数据量。 - 避免频繁排序:对需要多次排序的场景,考虑转换为有序集合(如
TreeSet
)。
6. limit(n) 和 skip(n):分页控制
作用:skip
跳过前N个元素,limit
限制返回数量。
示例:
List<Integer> result = Stream.iterate(0, n -> n + 1).skip(5) // 跳过0-4,从5开始.limit(10) // 取5-14.collect(Collectors.toList());
应用场景:
-
分页查询:模拟数据库分页。
int page = 2, size = 10; List<User> users = allUsers.stream().skip((page - 1) * size).limit(size).collect(Collectors.toList());
-
性能注意:对非顺序流(如并行流),
skip
和limit
可能无法保证预期结果。 -
3. 终端操作(Terminal Operations)
-
遍历元素:
forEach(Consumer<T>)
list.stream().forEach(System.out::println);
-
收集结果:
collect(Collector)
List<String> list = stream.collect(Collectors.toList()); Set<String> set = stream.collect(Collectors.toSet()); String joined = stream.collect(Collectors.joining(", "));
-
统计数量:
count()
long count = list.stream().filter(s -> s.length() > 3).count();
-
匹配检查:
anyMatch(Predicate<T>)
:至少一个元素匹配。allMatch(Predicate<T>)
:所有元素匹配。noneMatch(Predicate<T>)
:没有元素匹配。
boolean hasA = list.stream().anyMatch(s -> s.contains("a"));
-
查找元素:
findFirst()
:返回第一个元素(Optional<T>
)。findAny()
:适用于并行流,返回任意元素。
Optional<String> first = list.stream().findFirst();
-
归约操作:
reduce(BinaryOperator<T>)
Optional<Integer> sum = Stream.of(1, 2, 3).reduce(Integer::sum); // 6
三、高级用法
1. 分组与分区
-
分组:
Collectors.groupingBy()
Map<Integer, List<String>> groupByLength = list.stream().collect(Collectors.groupingBy(String::length)); // 按字符串长度分组
-
分区:
Collectors.partitioningBy()
Map<Boolean, List<String>> partition = list.stream().collect(Collectors.partitioningBy(s -> s.length() > 3)); // 按条件分为两组
2. 并行流处理
-
创建并行流:
.parallel()
或parallelStream()
。List<String> result = list.parallelStream().filter(s -> s.length() > 3).collect(Collectors.toList());
-
注意事项:
- 确保操作线程安全(如避免修改共享变量)。
- 并行流可能不适用于小数据量或复杂中间操作。
3. 原始类型流
-
避免装箱开销:使用
IntStream
,LongStream
,DoubleStream
。IntStream.range(1, 5).forEach(System.out::println); // 1, 2, 3, 4 LongStream.of(10L, 20L).sum();
四、实际应用场景示例
1. 数据转换与过滤
// 从用户列表中提取成年用户的姓名
List<String> adultNames = users.stream().filter(user -> user.getAge() >= 18).map(User::getName).collect(Collectors.toList());
2. 统计与汇总
// 计算订单总金额
double totalAmount = orders.stream().mapToDouble(Order::getAmount).sum();
3. 复杂集合处理
// 将多个订单的商品列表合并并去重
Set<String> allProducts = orders.stream().flatMap(order -> order.getProducts().stream()).collect(Collectors.toSet());
4. 分组统计
// 按部门分组统计员工平均工资
Map<String, Double> avgSalaryByDept = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment,Collectors.averagingDouble(Employee::getSalary));
五、注意事项与最佳实践
- 避免副作用:在流操作中不要修改外部变量,尤其是在并行流中。
- 优先使用无状态操作:如
filter
,map
比sorted
,distinct
更高效。 - 谨慎使用并行流:仅在数据量大且操作耗时的情况下考虑并行化。
- 减少装箱开销:对数值操作使用原始类型流(
IntStream
等)。 - 合理使用短路操作:如
findFirst()
,limit()
可提前终止流处理。
六、与传统循环的对比
场景 | 传统循环 | Stream 流 |
---|---|---|
简单遍历 | 直接易读 | 代码更简洁,但可能略微性能开销 |
复杂数据处理 | 需多层嵌套循环,代码冗长 | 链式调用,逻辑清晰 |
并行处理 | 需手动管理线程和同步 | 通过 .parallel() 自动并行化 |
函数式编程支持 | 需额外工具类配合 | 原生支持 Lambda 和方法引用 |
通过掌握 Stream 流的常见用法,可以显著提升代码的可读性和开发效率,尤其在处理集合数据时,能够以更简洁的方式实现复杂的数据操作。
相关文章:
Java 8 Stream 流操作全解析
文章目录 **一、Stream 流简介****二、Stream 流核心操作****1. 创建 Stream****2. 中间操作(Intermediate Operations)****filter(Predicate<T>):过滤数据****1. 简单条件过滤****2. 多条件组合****3. 过滤对象集合****4. 过滤 null 值…...
java线程中断的艺术
文章目录 引言java中的中断何时触发中断阻塞如何响应中断中断的一些实践基于标识取消任务如何处理阻塞式的中断合理的中断策略时刻保留中断的状态超时任务取消的最优解处理系统层面阻塞IO小结参考引言 我们通过并发编程提升了系统的吞吐量,特定场景下我们希望并发的线程能够及…...
【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、进度类计算的基本概念1.1 前导图法1.2 箭线图法1.3 时标网络图1.4 确定依赖关系1.5 提前量与滞后量1.6 关键路径法1.7 总浮动时间1.8 自由浮动时间1.9 关键链法1.10 资源优化技术1.11 进度压缩二、基本公式…...
HarmonyOS 鸿蒙应用开发基础:转换整个PDF文档为图片功能
在许多应用场景中,将PDF文档的每一页转换为单独的图片文件是非常有帮助的。这可以用于文档的分享、扫描文档的电子化存档、或者进行进一步的文字识别处理等。本文将介绍如何使用华为HarmonyOS提供的PDF处理服务将整个PDF文档转换为图片,并将这些图片存放…...
Flask-SQLAlchemy核心概念:模型类与数据库表、类属性与表字段、外键与关系映射
前置阅读,关于Flask-SQLAlchemy支持哪些数据库及基本配置,链接:Flask-SQLAlchemy_数据库配置 摘要 本文以一段典型的 SQLAlchemy 代码示例为引入,阐述以下核心概念: 模型类(Model Class) ↔ 数…...
刷题 | 牛客 - js中等题-下(更ing)30/54知识点解答
知识点汇总: 数组: Array.prototype.pop():从数组末尾删除一个元素,并返回这个元素。 Array.prototype.shift():从数组开头删除一个元素,并返回这个元素。 array.reverse():将数组元素反转顺…...
RAM(随机存取存储器)的通俗解释及其在路由器中的作用
RAM(随机存取存储器)的通俗解释及其在路由器中的作用 一、RAM是什么? RAM(Random Access Memory) 就像餐厅的“临时工作台”: 核心作用:临时存储正在处理的任务(如厨师同时处理多道…...

六、【前端启航篇】Vue3 项目初始化与基础布局:搭建美观易用的管理界面骨架
【前端启航篇】Vue3 项目初始化与基础布局:搭建美观易用的管理界面骨架 前言技术选型回顾与准备准备工作第一步:进入前端项目并安装 Element Plus第二步:在 Vue3 项目中引入并配置 Element Plus第三步:设计基础页面布局组件第四步…...
【项目需求分析文档】:在线音乐播放器(Online-Music)
1. 用户管理模块 1.1 注册功能 功能描述 提供注册页面,包含用户名、密码输入框及提交按钮。用户名需唯一性校验,密码使用 BCrypt 加密算法存储。注册成功后自动跳转至登录页面。 1.2 登录功能 功能描述 提供登录页面,包含用户名、密码输入…...

C++ 前缀和数组
一. 一维数组前缀和 1.1. 定义 前缀和算法通过预处理数组,计算从起始位置到每个位置的和,生成一个新的数组(前缀和数组)。利用该数组,可以快速计算任意区间的和,快速求出数组中某一段连续区间的和。 1.2. …...
PHP 实现通用数组字段过滤函数:灵活去除或保留指定 Key
PHP 实现数组去除或保留指定字段的通用函数详解 一、文章标题 《PHP 实现通用数组字段过滤函数:灵活去除或保留指定 Key》 二、摘要 在实际开发中,我们经常需要对数组进行字段级别的操作,例如从一个数组中删除某些敏感字段(如密码、token),或者只保留特定字段用于接口…...
NACOS2.3.0开启鉴权登录
环境 名称版本nacos2.3.0(Linux)java java version "17.0.14" 2025-01-21 LTS # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use thi…...

细胞冻存的注意事项,细胞冻存试剂有哪些品牌推荐
细胞冻存的原理 细胞冻存的基本原理是利用低温环境抑制细胞的新陈代谢,使细胞进入一种“休眠”状态。在低温条件下,细胞的生物活动几乎停止,从而实现长期保存。然而,细胞在冷冻过程中可能会因为细胞内外水分结冰形成冰晶而受损。…...

快速上手Linux火墙管理
实验网络环境: 主机IP网络f1192.168.42.129/24NATf2(双网卡) 192.168.42.128/24 192.168.127.20/24 NAT HOST-NOLY f3192.168.127.30/24HOST-ONLY 一、iptables服务 1.启用iptables服务 2.语法格式及常用参数 语法格式:参数&…...

[创业之路-375]:企业战略管理案例分析 - 华为科技巨擘的崛起:重构全球数字化底座的超级生命体
在人类文明从工业时代(机械、电气、自动化)迈向数字智能(硬件、软件、算法、虚拟、智能)时代的临界点上,一家中国企业正以令人震撼的姿态重塑全球科技版图。从通信网络的底层架构到智能终端的生态闭环,从芯…...
【paddle】常见的数学运算
根据提供的 PaddlePaddle 函数列表,我们可以将它们按照数学运算、逻辑运算、三角函数、特殊函数、统计函数、张量操作和其他操作等类型进行分类。以下是根据函数功能进行的分类: 取整运算 Rounding functions 代码描述round(x)距离 x 最近的整数floor(…...

AI基础知识(05):模型提示词、核心设计、高阶应用、效果增强
目录 一、核心设计原则 二、高阶应用场景 三、突破性技巧 以下是针对DeepSeek模型的提示词设计思路及典型应用场景示例,帮助挖掘其潜在能力: 一、核心设计原则 1. 需求明确化:用「角色定位任务目标输出格式」明确边界 例:作为历…...
分布式事务之Seata
概述 Seata有四种模式 AT模式:无侵入式的分布式事务解决方案,适合不希望对业务进行改造的场景,但由于需要添加全局事务锁,对影响高并发系统的性能。该模式主要关注多DB访问的数据一致性,也包括多服务下的多DB数据访问…...

推测解码算法在 MTT GPU 的应用实践
前言 目前主流的大模型自回归解码每一步都只生成一个token, 尽管kv cache等技术可以提升解码的效率,但是单个样本的解码速度依然受限于访存瓶颈,即模型需要频繁从内存中读取和写入数据,此时GPU的利用率有限。为了解决这种问题,…...

Axure酒店管理系统原型
酒店管理系统通常被设计为包含多个模块或界面,以支持酒店运营的不同方面和参与者。其中,管理端和商户端是两个核心组成部分,它们各自承担着不同的职责和功能。 软件版本:Axure RP 9 预览地址:https://556i1e.axshare.…...

写实交互数字人在AI招聘中的应用方案
随着科技的进步,越来越多的行业开始探索如何利用人工智能提升效率和服务质量。其中,写实交互数字人技术以其高度拟真的交互体验和丰富的情感表达能力,在人力资源领域特别是招聘环节中展现出了巨大潜力。本文将探讨写实交互数字人在AI招聘中的…...
C++中IO类(iostream、fstream和sstream)知识详解和应用
一、C I/O 类体系概览 C 的 I/O 功能由一组 流(stream) 类封装,位于头文件 <iostream>、<fstream>、<sstream> 等。核心类别及其继承关系简图如下: ios_base↑basic_ios<CharT,Traits>↑┌───────…...
Spring Boot中如何对密码等敏感信息进行脱敏处理
以下是常见的脱敏方法及实现步骤,涵盖配置、日志和API响应等多个层面: 1. 配置文件敏感信息脱敏 (1) 使用加密库(如Jasypt) 步骤: 添加依赖: <dependency><groupId>com.github.ulise…...
React从基础入门到高级实战:React 基础入门 - JSX与组件基础
JSX 与组件基础 引言 在 React 开发中,JSX 和 组件 是两个最基础且核心的概念。JSX 是一种独特的语法,让你在 JavaScript 中编写类似 HTML 的代码,而组件则是 React 应用的基本构建块,帮助你将复杂的界面拆分为可复用的模块。本…...

房贷利率计算前端小程序
利率计算前端小程序 视图效果展示如下: 在这里插入代码片 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&qu…...

在Visual Studio中进行cuda编程
首先下载与CUDA Toolkit匹配的Visual Studio版本 比如我的CUDA Toolkit版本是12.6,那么我可以使用2022的Visual Studio。 查看Toolkit版本 nvcc -V 配置 ok,让我们开始Visual Studio的nvcc编译器配置 参考例文https://github.com/apachecn/succinc…...

Fastrace:Rust 中分布式追踪的现代化方案
原文链接:Fastrace: A Modern Approach to Distributed Tracing in Rust | FastLabs / Blog 摘要 在微服务架构中,分布式追踪对于理解应用程序的行为至关重要。虽然 tokio-rs/tracing 在 Rust 中被广泛使用,但它存在一些显著的挑战…...

Linux云计算训练营笔记day13【CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM】
Linux云计算训练营笔记day13[CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM]] 目录 Linux云计算训练营笔记day13[CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM]]1.find练习2.vim高级使用2.1 命令模式:2.2 插入模式:2.3 末行模式: 3. vimdiff4. ping5.…...

黑马Java基础笔记-15
Set 无索引,无序,不可重复 HashSet object类中默认hashCode的方法是根据地址值。 如果集合中存储的是自定义对象,必须要重写hashCode和equals方法。 底层原理 jdk8以前:数组 链表 jdk8及以后:数组 链表 红黑…...
Elasticsearch简单集成java框架方式。
Elasticsearch 在 Java 中最常用的客户端是什么?如何初始化一个 RestHighLevelClient?如何用 Spring Boot 快速集成 Elasticsearch?Spring Data Elasticsearch 如何定义实体类与索引的映射? 最常用的 Java 客户端 目前官方推荐使用…...