Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
    • 基本类型与包装类
    • Object类与常用方法
    • 泛型
    • 枚举与注解
    • 反射
    • 值传递与引用
    • Lambda表达式与函数式接口
    • Stream API
      • 1. Stream 模型
      • 2. Stream 不是集合
      • 3. 中间操作与终止操作
      • 4. filter 与 map
      • 5. flatMap
      • 6. collect 与分组
      • 7. reduce
      • 8. 并行流
      • 9. 常见坑
        • 9.1 在 Stream 中做副作用
        • 9.2 toMap key 重复
        • 9.3 过度链式调用
      • 10. 执行机制与性能边界
      • Tips 快问快答
    • Optional与空值处理
    • 日期时间API
    • Java现代语法特性
  • Java集合

  • Java并发

  • Java IO

  • JVM

目录

Stream API

Stream API 是 Java 8 引入的数据处理 API,用于以声明式方式完成过滤、映射、排序、分组、聚合等操作。它不是集合,也不存储数据,而是一条数据处理流水线。

# 1. Stream 模型

数据源
  │
  ▼
中间操作 filter / map / sorted
  │
  ▼
终止操作 collect / count / forEach

示例:

List<String> names = users.stream()
        .filter(User::isActive)
        .map(User::getName)
        .toList();

这段代码表达的是:从用户列表中筛选活跃用户,再提取姓名。

# 2. Stream 不是集合

对比项 Collection Stream
是否存储数据 是 否
是否可重复遍历 可以 通常只能消费一次
关注点 数据容器 数据处理流程
操作方式 增删改查 过滤、转换、聚合

Stream 被终止操作消费后不能再次使用。

Stream<User> stream = users.stream();
stream.count();
stream.toList(); // IllegalStateException

# 3. 中间操作与终止操作

中间操作是惰性的,不会立即执行。

Stream<User> stream = users.stream()
        .filter(user -> {
            System.out.println(user.getName());
            return user.isActive();
        });

没有终止操作时,filter 不会执行。

常见中间操作:

操作 作用
filter 过滤
map 映射
flatMap 扁平化
distinct 去重
sorted 排序
limit 截断
skip 跳过

常见终止操作:

操作 作用
collect 收集
toList 转列表
count 计数
forEach 遍历消费
reduce 归约
anyMatch 任意匹配
allMatch 全部匹配
findFirst 找第一个

# 4. filter 与 map

List<Long> userIds = users.stream()
        .filter(User::isActive)
        .map(User::getId)
        .toList();

流程:

User
  │ filter active
  ▼
Active User
  │ map getId
  ▼
Long id

filter 改变元素数量,map 改变元素形态。

# 5. flatMap

flatMap 用于把多层结构摊平。

List<String> tags = users.stream()
        .flatMap(user -> user.getTags().stream())
        .distinct()
        .toList();

结构:

Stream&lt;List&lt;Tag>>
  │ flatMap
  ▼
Stream&lt;Tag>

适合处理一对多关系。

# 6. collect 与分组

按状态分组:

Map<OrderStatus, List<Order>> group = orders.stream()
        .collect(Collectors.groupingBy(Order::getStatus));

统计数量:

Map<OrderStatus, Long> countMap = orders.stream()
        .collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));

转 Map:

Map<Long, User> userMap = users.stream()
        .collect(Collectors.toMap(User::getId, Function.identity()));

如果 key 可能重复,必须提供合并函数:

Map<Long, User> userMap = users.stream()
        .collect(Collectors.toMap(
                User::getId,
                Function.identity(),
                (oldValue, newValue) -> newValue
        ));

# 7. reduce

reduce 用于归约。

int total = numbers.stream()
        .reduce(0, Integer::sum);

流程:

初始值 0
  │ + 1
  ▼
1
  │ + 2
  ▼
3
  │ + 3
  ▼
6

简单求和可优先使用基本类型流:

int total = users.stream()
        .mapToInt(User::getAge)
        .sum();

# 8. 并行流

users.parallelStream()
        .map(this::calculate)
        .toList();

并行流适合 CPU 密集、数据量较大、无共享状态的计算。

不适合:

  • IO 阻塞。
  • 数据量小。
  • 修改共享集合。
  • 依赖 ThreadLocal 上下文。
  • 需要精确控制线程池。

并行流默认使用公共 ForkJoinPool,生产中要谨慎。

# 9. 常见坑

# 9.1 在 Stream 中做副作用

不推荐:

List<Long> ids = new ArrayList<>();
users.stream().forEach(user -> ids.add(user.getId()));

推荐:

List<Long> ids = users.stream()
        .map(User::getId)
        .toList();

# 9.2 toMap key 重复

Collectors.toMap 遇到重复 key 会抛异常,必须提供合并函数。

# 9.3 过度链式调用

Stream 链太长、Lambda 过复杂会降低可读性。复杂逻辑应抽方法。

# 10. 执行机制与性能边界

Stream 的执行可以理解为“声明一条流水线,终止操作触发执行”。中间操作并不会一个阶段一个阶段地创建完整集合,而是尽量把多个操作融合到一次遍历中。

数据元素 1
  │ filter
  │ map
  │ collect
  ▼
结果容器

数据元素 2
  │ filter
  │ map
  │ collect
  ▼
结果容器

这也是 Stream 在很多场景中既表达清晰又不会产生大量中间集合的原因。

操作类型 是否有状态 说明
filter 无状态 每个元素独立判断
map 无状态 每个元素独立转换
distinct 有状态 需要记住已出现元素
sorted 有状态 需要缓存并排序
limit 短路 达到数量后可停止
anyMatch 短路 找到满足元素即可停止

资深开发者使用 Stream 时会关注三件事:

  1. 这条链是否比普通循环更清晰。
  2. 是否引入隐藏副作用,例如修改外部集合、写数据库、发 RPC。
  3. 是否因为 sorted、distinct、groupingBy 等操作带来额外内存和复杂度。

复杂业务规则可以保留 Stream 的骨架,但把规则抽成具名方法:

List<Order> riskyOrders = orders.stream()
        .filter(this::isRiskyOrder)
        .sorted(Comparator.comparing(Order::createdAt))
        .toList();

这样既保留声明式流程,又能让规则单独测试。

# Tips 快问快答

Q:Stream 是集合吗? A:不是。Stream 是数据处理流水线,不存储数据。

Q:Stream 能重复使用吗? A:不能,被终止操作消费后通常不能再次使用。

Q:中间操作什么时候执行? A:遇到终止操作时才执行。

Q:filter 和 map 有什么区别? A:filter 过滤元素,map 转换元素。

Q:flatMap 解决什么问题? A:把嵌套流或多层集合摊平成一层。

Q:toMap key 重复会怎样? A:默认抛异常,需要提供合并函数。

Q:parallelStream 一定更快吗? A:不一定,要看数据量、任务类型和线程池影响。

Q:Stream 中适合做复杂业务吗? A:不适合,复杂逻辑应抽成具名方法。

上次更新: 2026/06/25, 14:19:18
Lambda表达式与函数式接口
Optional与空值处理

← Lambda表达式与函数式接口 Optional与空值处理→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式