Java 8 Stream 流操作全解析

news2025/5/25 22:10:39

文章目录

    • **一、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 的核心特性
  1. 一对一转换:每个输入元素对应一个输出元素,元素数量不变。
  2. 类型转换:输入类型 T 可转换为任意输出类型 R(如 StringInteger)。
  3. 惰性求值:只有终端操作触发时才会执行映射逻辑。
  4. 无副作用:理想情况下,映射函数不修改外部状态(符合函数式编程原则)。

作用:将元素转换为另一种类型或提取特定属性。

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;
        @Override
        public boolean equals(Object o) { /* 基于id比较 */ }
        @Override
        public 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());
    
  • 性能注意:对非顺序流(如并行流),skiplimit 可能无法保证预期结果。

  • 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)
    );

五、注意事项与最佳实践
  1. 避免副作用:在流操作中不要修改外部变量,尤其是在并行流中。
  2. 优先使用无状态操作:如 filter, mapsorted, distinct 更高效。
  3. 谨慎使用并行流:仅在数据量大且操作耗时的情况下考虑并行化。
  4. 减少装箱开销:对数值操作使用原始类型流(IntStream 等)。
  5. 合理使用短路操作:如 findFirst(), limit() 可提前终止流处理。

六、与传统循环的对比
场景传统循环Stream 流
简单遍历直接易读代码更简洁,但可能略微性能开销
复杂数据处理需多层嵌套循环,代码冗长链式调用,逻辑清晰
并行处理需手动管理线程和同步通过 .parallel() 自动并行化
函数式编程支持需额外工具类配合原生支持 Lambda 和方法引用

通过掌握 Stream 流的常见用法,可以显著提升代码的可读性和开发效率,尤其在处理集合数据时,能够以更简洁的方式实现复杂的数据操作。

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

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

相关文章

六、【前端启航篇】Vue3 项目初始化与基础布局:搭建美观易用的管理界面骨架

【前端启航篇】Vue3 项目初始化与基础布局&#xff1a;搭建美观易用的管理界面骨架 前言技术选型回顾与准备准备工作第一步&#xff1a;进入前端项目并安装 Element Plus第二步&#xff1a;在 Vue3 项目中引入并配置 Element Plus第三步&#xff1a;设计基础页面布局组件第四步…

C++ 前缀和数组

一. 一维数组前缀和 1.1. 定义 前缀和算法通过预处理数组&#xff0c;计算从起始位置到每个位置的和&#xff0c;生成一个新的数组&#xff08;前缀和数组&#xff09;。利用该数组&#xff0c;可以快速计算任意区间的和&#xff0c;快速求出数组中某一段连续区间的和。 1.2. …

细胞冻存的注意事项,细胞冻存试剂有哪些品牌推荐

细胞冻存的原理 细胞冻存的基本原理是利用低温环境抑制细胞的新陈代谢&#xff0c;使细胞进入一种“休眠”状态。在低温条件下&#xff0c;细胞的生物活动几乎停止&#xff0c;从而实现长期保存。然而&#xff0c;细胞在冷冻过程中可能会因为细胞内外水分结冰形成冰晶而受损。…

快速上手Linux火墙管理

实验网络环境&#xff1a; 主机IP网络f1192.168.42.129/24NATf2&#xff08;双网卡&#xff09; 192.168.42.128/24 192.168.127.20/24 NAT HOST-NOLY f3192.168.127.30/24HOST-ONLY 一、iptables服务 1.启用iptables服务 2.语法格式及常用参数 语法格式&#xff1a;参数&…

[创业之路-375]:企业战略管理案例分析 - 华为科技巨擘的崛起:重构全球数字化底座的超级生命体

在人类文明从工业时代&#xff08;机械、电气、自动化&#xff09;迈向数字智能&#xff08;硬件、软件、算法、虚拟、智能&#xff09;时代的临界点上&#xff0c;一家中国企业正以令人震撼的姿态重塑全球科技版图。从通信网络的底层架构到智能终端的生态闭环&#xff0c;从芯…

AI基础知识(05):模型提示词、核心设计、高阶应用、效果增强

目录 一、核心设计原则 二、高阶应用场景 三、突破性技巧 以下是针对DeepSeek模型的提示词设计思路及典型应用场景示例&#xff0c;帮助挖掘其潜在能力&#xff1a; 一、核心设计原则 1. 需求明确化&#xff1a;用「角色定位任务目标输出格式」明确边界 例&#xff1a;作为历…

推测解码算法在 MTT GPU 的应用实践

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

Axure酒店管理系统原型

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

写实交互数字人在AI招聘中的应用方案

随着科技的进步&#xff0c;越来越多的行业开始探索如何利用人工智能提升效率和服务质量。其中&#xff0c;写实交互数字人技术以其高度拟真的交互体验和丰富的情感表达能力&#xff0c;在人力资源领域特别是招聘环节中展现出了巨大潜力。本文将探讨写实交互数字人在AI招聘中的…

房贷利率计算前端小程序

利率计算前端小程序 视图效果展示如下&#xff1a; 在这里插入代码片 <!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&#xff0c;那么我可以使用2022的Visual Studio。 查看Toolkit版本 nvcc -V 配置 ok&#xff0c;让我们开始Visual Studio的nvcc编译器配置 参考例文https://github.com/apachecn/succinc…

Fastrace:Rust 中分布式追踪的现代化方案

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

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 无索引&#xff0c;无序&#xff0c;不可重复 HashSet object类中默认hashCode的方法是根据地址值。 如果集合中存储的是自定义对象&#xff0c;必须要重写hashCode和equals方法。 底层原理 jdk8以前&#xff1a;数组 链表 jdk8及以后&#xff1a;数组 链表 红黑…

软件设计师“排序算法”真题考点分析——求三连

一、考点分值占比与趋势分析 综合知识题分值统计表 年份考题数量总分值分值占比考察重点2018222.67%时间复杂度/稳定性判断2019334.00%算法特性对比分析2020222.67%空间复杂度要求2021111.33%算法稳定性判断2022334.00%综合特性应用2023222.67%时间复杂度计算2024222.67%分治…

Visual Studio 2019/2022:当前不会命中断点,还没有为该文档加载任何符号。

1、打开调试的模块窗口&#xff0c;该窗口一定要在调试状态下才会显示。 vs2019打开调试的模块窗口 2、Visual Studio 2019提示未使用调试信息生成二进制文件 未使用调试信息生成二进制文件 3、然后到debug目录下看下确实未生成CoreCms.Net.Web.WebApi.pdb文件。 那下面的…

vue--ofd/pdf预览实现

背景 实现预览ofd/pdf超链接功能 业务实现 pdf的预览 实现方式&#xff1a; 直接使用 <iframe :src"${url}#navpanes0&toolbar0" /> 实现pdf的预览。 navpanes0 隐藏侧边栏toolbar0 隐藏顶部工具栏 使用pdf.js&#xff0c;代码先行&#xff1a; <tem…

Python 爬虫之requests 模块的应用

requests 是用 python 语言编写的一个开源的HTTP库&#xff0c;可以通过 requests 库编写 python 代码发送网络请求&#xff0c;其简单易用&#xff0c;是编写爬虫程序时必知必会的一个模块。 requests 模块的作用 发送网络请求&#xff0c;获取响应数据。 中文文档&#xf…

【MySQL】CRUD

CRUD 简介 CRUD是对数据库中的记录进行基本的增删改查操作 Create&#xff08;创建&#xff09;Retrieve&#xff08;读取&#xff09;Update&#xff08;更新&#xff09;Delete&#xff08;删除&#xff09; 一、新增&#xff08;Create&#xff09; 语法&#xff1a; I…

Spring Boot微服务架构(三):Spring Initializr创建CRM项目

使用Spring Initializr创建CRM项目 一、创建项目前的准备 访问Spring Initializr网站&#xff1a; 打开浏览器访问 https://start.spring.io/或者直接使用IDE&#xff08;如IntelliJ IDEA或Eclipse&#xff09;内置的Spring Initializr功能 项目基本信息配置&#xff1a; Proj…