作为一名 Java 开发工程师,你一定在项目中处理过大量的集合数据,比如:过滤、转换、分组、统计等操作。在 Java 8 之前,这些操作往往需要编写大量冗余的 for 循环和 if 判断。

Java Stream API 的引入,彻底改变了集合操作的方式 —— 它提供了一种声明式、链式、可并行的编程模型,让你的代码更简洁、更高效、更易读。

本文将带你全面掌握:

  • Java Stream 的基本概念与生命周期
  • 常用中间操作与终端操作(filter、map、sorted、collect 等)
  • Stream 的并行流与性能优化
  • Stream 与集合、Optional、函数式接口的结合使用
  • 实战场景讲解(如数据过滤、分组统计、合并、去重等)
  • 最佳实践与常见误区

并通过丰富的代码示例和真实项目场景讲解,帮助你写出更优雅、结构更清晰的 Java 函数式代码。


🧱 一、什么是 Java Stream?

Stream 是 Java 8 引入的一个函数式编程接口,它不是集合本身,而是对集合(如 ListSet)或数组的函数式封装,用于进行数据的筛选、映射、聚合等操作

✅ Stream 的核心特性:

特性 描述
不存储数据 Stream 只是数据源的视图,不保存数据本身
不可重复使用 一个 Stream 只能使用一次,之后会抛出异常
支持链式调用 多个操作可以以声明式方式串联
支持串行与并行 可以使用 parallelStream() 提高性能
基于函数式接口 支持 Lambda 表达式,如 PredicateFunctionConsumer 等

🔍 二、Stream 的生命周期

一个完整的 Stream 操作流程包括三个阶段:

  1. 获取源(Source)
  2. 中间操作(Intermediate Operations)
  3. 终端操作(Terminal Operation)
// 示例:Stream 的完整流程
List<String> filtered = list.stream()
    .filter(s -> s.startsWith("A"))  // 中间操作
    .map(String::toUpperCase)        // 中间操作
    .limit(5)                        // 中间操作
    .toList();                       // 终端操作

🧠 三、Stream 的创建方式

创建方式 示例
从集合创建 list.stream()
从数组创建 Arrays.stream(array)
从值创建 Stream.of("A", "B", "C")
从文件行创建 Files.lines(Paths.get("file.txt"))
无限流创建 Stream.iterate(0, n -> n + 1)Stream.generate(Math::random)

🧩 四、常用中间操作(Intermediate Operations)

操作 描述 示例
filter(Predicate) 过滤符合条件的元素 stream.filter(s -> s.length() > 3)
map(Function) 转换每个元素 stream.map(String::toUpperCase)
flatMap(Function) 扁平化处理,适用于嵌套结构 stream.flatMap(List::stream)
distinct() 去重(基于 equals() 和 hashCode() stream.distinct()
sorted() 排序(自然排序或自定义排序) stream.sorted(Comparator.reverseOrder())
limit(n) 截取前 n 个元素 stream.limit(10)
skip(n) 跳过前 n 个元素 stream.skip(5)

🧨 五、常用终端操作(Terminal Operations)

操作 描述 示例
forEach(Consumer) 遍历每个元素 stream.forEach(System.out::println)
collect(Collector) 收集结果(转为 List、Set、Map 等) stream.collect(Collectors.toList())
count() 返回元素个数 stream.count()
anyMatch(Predicate) 是否有任意一个匹配 stream.anyMatch(s -> s.contains("Java"))
allMatch(Predicate) 是否全部匹配 stream.allMatch(s -> s.length() > 3)
noneMatch(Predicate) 是否没有匹配 stream.noneMatch(s -> s.isEmpty())
findFirst() 获取第一个元素 stream.findFirst()
reduce(BinaryOperator) 聚合操作(如求和) stream.reduce((a, b) -> a + b)

🧪 六、Stream 的实战应用场景

场景1:数据过滤与转换(最常见用途)

List<String> filtered = list.stream()
    .filter(s -> s.startsWith("A"))
    .map(String::toUpperCase)
    .toList();

场景2:统计与聚合(如求和、平均值)

int sum = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();

double avg = numbers.stream()
    .mapToDouble(Integer::doubleValue)
    .average()
    .orElse(0);

场景3:去重与排序

List<String> uniqueSorted = list.stream()
    .distinct()
    .sorted()
    .toList();

场景4:分组统计(Group By)

Map<String, List<User>> grouped = users.stream()
    .collect(Collectors.groupingBy(User::getRole));

场景5:多条件分组(Group By 多字段)

Map<String, Map<Integer, List<User>>> grouped = users.stream()
    .collect(Collectors.groupingBy(User::getCity,
             Collectors.groupingBy(User::getAge)));

场景6:分区(Partition By 条件)

Map<Boolean, List<User>> partitioned = users.stream()
    .collect(Collectors.partitioningBy(u -> u.getAge() > 18));

场景7:合并多个集合

List<String> merged = Stream.of(list1, list2, list3)
    .flatMap(List::stream)
    .distinct()
    .toList();

场景8:并行流提升性能(大数据量推荐)

int sum = numbers.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

🚫 七、常见误区与注意事项

误区 正确做法
在 Stream 中修改原始集合 Stream 是只读的,不能直接修改源数据
多次使用同一个 Stream Stream 只能使用一次,重复使用会抛出异常
忘记关闭流式资源(如 IO) 使用完文件流等资源应关闭
忽略空值处理(如 findFirst() 使用 Optional 判断是否存在
忽略并行流的线程安全问题 并行流不适合有状态操作
忽略中间操作的惰性求值 中间操作不会立即执行,只有终端操作才会触发

📊 八、总结:Java Stream 核心知识点一览表

内容 说明
接口定义 java.util.stream.Stream
创建方式 从集合、数组、值、生成器等
中间操作 filter、map、flatMap、distinct、sorted、limit、skip
终端操作 forEach、collect、count、anyMatch、reduce
常用收集器 toList、toSet、groupingBy、partitioningBy
并行流 parallelStream()
应用场景 数据过滤、转换、分组、统计、聚合
注意事项 不可重复使用、不能修改源数据、避免中间操作副作用

📎 九、附录:Stream 常用技巧速查表

技巧 示例
转为 List stream.toList()
转为 Set stream.collect(Collectors.toSet())
转为 Map stream.collect(Collectors.toMap(User::getId, Function.identity()))
获取最大最小值 stream.max(Comparator.naturalOrder())
判断是否包含 stream.anyMatch(s -> s.equals("Java"))
使用 Stream 合并字符串 stream.collect(Collectors.joining(","))
使用 Stream 生成范围数字 IntStream.range(1, 10).boxed().toList()
使用 Stream 合并多个集合 Stream.of(list1, list2).flatMap(List::stream).toList()
使用 Stream 处理 Optional stream.flatMap(s -> Optional.ofNullable(s))
使用 Stream 处理异常 stream.filter(s -> { try { ... } catch (...) { return false; } })

如果你正在准备一篇面向初学者的技术博客,或者希望系统回顾 Java 8 的函数式编程特性,这篇文章将为你提供完整的知识体系和实用的编程技巧。

欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的 Stream 相关问题。我们下期再见 👋

📌 关注我,获取更多Java核心技术深度解析!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐