当前位置: 首页 > article >正文

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 的核心特性
  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;@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());
    
  • 性能注意:对非顺序流(如并行流),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.lryc.cn/news/2385642.html

相关文章:

  • java线程中断的艺术
  • 【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算
  • HarmonyOS 鸿蒙应用开发基础:转换整个PDF文档为图片功能
  • Flask-SQLAlchemy核心概念:模型类与数据库表、类属性与表字段、外键与关系映射
  • 刷题 | 牛客 - js中等题-下(更ing)30/54知识点解答
  • RAM(随机存取存储器)的通俗解释及其在路由器中的作用
  • 六、【前端启航篇】Vue3 项目初始化与基础布局:搭建美观易用的管理界面骨架
  • 【项目需求分析文档】:在线音乐播放器(Online-Music)
  • C++ 前缀和数组
  • PHP 实现通用数组字段过滤函数:灵活去除或保留指定 Key
  • NACOS2.3.0开启鉴权登录
  • 细胞冻存的注意事项,细胞冻存试剂有哪些品牌推荐
  • 快速上手Linux火墙管理
  • [创业之路-375]:企业战略管理案例分析 - 华为科技巨擘的崛起:重构全球数字化底座的超级生命体
  • 【paddle】常见的数学运算
  • AI基础知识(05):模型提示词、核心设计、高阶应用、效果增强
  • 分布式事务之Seata
  • 推测解码算法在 MTT GPU 的应用实践
  • Axure酒店管理系统原型
  • 写实交互数字人在AI招聘中的应用方案
  • C++中IO类(iostream、fstream和sstream)知识详解和应用
  • Spring Boot中如何对密码等敏感信息进行脱敏处理
  • React从基础入门到高级实战:React 基础入门 - JSX与组件基础
  • 房贷利率计算前端小程序
  • 在Visual Studio中进行cuda编程
  • Fastrace:Rust 中分布式追踪的现代化方案
  • Linux云计算训练营笔记day13【CentOS 7 find、vim、vimdiff、ping、wget、curl、RPM、YUM】
  • 黑马Java基础笔记-15
  • Elasticsearch简单集成java框架方式。
  • 【RAG文档切割】从基础拆分到语义分块实战指南