Java 8 最重要的 API:Stream 全面解析与实战指南

2 阅读3分钟

Java 8 最重要的 API:Stream 全面解析与实战指南

在 Java 8 中,Stream(流) 是最具革命性的 API 之一,它将 Java 编程从“命令式”风格,带入了更简洁、更高效的“函数式”风格。

本文将从基础概念、核心 API、典型案例到最佳实践,系统梳理 Stream 的使用方式。


一、什么是 Stream(流)?

Stream 本质上是对数据集合的一种抽象视图,它不是数据本身,而是对数据进行声明式处理的流水线

核心特点:

  • 不存储数据(数据来源于集合、数组等)
  • 支持函数式操作(filter、map 等)
  • 惰性执行(中间操作不会立即执行)
  • 一次性消费(一个流只能使用一次)

二、从命令式到函数式:对比案例

1. Java 8 之前写法(命令式)

需求:筛选出姓张的用户 → 按年龄排序 → 获取姓名列表

List<User> users = getUsers();
List<User> zhangUsers = new ArrayList<>();

for (User user : users) {
    if (user.name.startsWith("张")) {
        zhangUsers.add(user);
    }
}

Collections.sort(zhangUsers, new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return o1.age - o2.age;
    }
});

List<String> names = new ArrayList<>();
for (User user : zhangUsers) {
    names.add(user.name);
}

问题:

  • 代码冗长
  • 关注“怎么做”(过程),而不是“做什么”(意图)
  • 易出 bug

2. Stream 写法(函数式)

users.stream()
    .filter(User::isSurnameZhang)
    .sorted(Comparator.comparing(User::getAge))
    .map(User::getName)
    .collect(Collectors.toList());

优势:

  • 一行表达完整逻辑
  • 可读性强(接近自然语言)
  • 更易维护与组合

三、Stream 的创建方式

常见创建方式:

Collection.stream()      // 集合
Stream.of(...)           // 显式创建
String.chars()           // 字符流
IntStream.range()        // 数值范围

示例:

IntStream.range(1, 10)

四、Stream 的操作分类

Stream 操作分为两类:

1. 中间操作(返回 Stream)

  • filter(过滤)
  • map(映射)
  • sorted(排序)
  • flatMap(扁平化)

特点:惰性执行


2. 终结操作(返回非 Stream)

  • forEach
  • count / max / min
  • findFirst / findAny
  • anyMatch / noneMatch
  • collect

特点:触发执行

⚠️ 注意:一个流只能被消费一次


五、核心操作详解

1. filter(过滤)

.filter(user -> user.getAge() >= 60)

2. map(映射)

将一个对象转换为另一个对象:

.map(User::getName)

3. sorted(排序)

.sorted(Comparator.comparing(User::getAge))

4. flatMap(扁平化)

list.stream()
    .map(s -> s.split(" "))
    .flatMap(Stream::of)
    .collect(Collectors.toList());

5. anyMatch(短路操作)

boolean result = users.stream()
    .anyMatch(user -> user.getName().startsWith("张"));

6. collect(最强大的终结操作)

.collect(Collectors.toList());

常见用法:

  • toList()
  • toSet()
  • toCollection()

示例(使用 TreeSet):

TreeSet<String> result = users.stream()
    .map(User::getName)
    .collect(Collectors.toCollection(TreeSet::new));

7. groupingBy(分组)

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

8. joining(字符串拼接)

String result = words.stream()
    .filter(w -> w.length() == 1)
    .collect(Collectors.joining(","));

六、Optional 的正确使用方式

推荐用法(链式调用)

User user = users.stream()
    .filter(User::isSurnameZhang)
    .findAny()
    .orElseThrow(IllegalAccessError::new);

❌ 错误用法(当作 null)

Optional<User> optionalUser = users.stream().findAny();

if (optionalUser.isPresent()) {
    optionalUser.get();
}

核心原则:

  • Optional 用于返回值表达“可能为空”
  • 不用于替代 if-null 逻辑

七、实用案例

1. 统计大写字母数量

public static int countUpperCaseLetters(String str) {
    return (int) str.chars()
        .filter(Character::isUpperCase)
        .count();
}

2. 条件筛选 + 排序 + 收集

public static LinkedList<String> collectNames(List<User> users) {
    return users.stream()
        .filter(user -> user.getAge() >= 60)
        .sorted(Comparator.comparing(User::getAge).reversed())
        .map(User::getName)
        .collect(Collectors.toCollection(LinkedList::new));
}

3. 分组 + 排序

employees.stream()
    .sorted(Comparator.comparing(Employee::getAge))
    .collect(Collectors.groupingBy(Employee::getDepartment));

4. 转 Map

Map<Integer, Order> map =
    orders.stream()
          .collect(Collectors.toMap(Order::getId, item -> item));

八、并行流(parallelStream)

示例:

IntStream.range(1, 1_000_000)
    .parallel()
    .filter(Primes::isPrime)
    .count();

特点:

  • 利用多核 CPU 并行计算
  • 理论上接近线性性能提升

⚠️ 注意事项:

  • 仅适用于无状态、无副作用操作
  • 必须进行性能测试
  • 默认并行度 ≈ CPU 核数 - 1

👉 结论:不确定就不要用


九、调试技巧

推荐使用插件:

  • Java Stream Debugger

可以可视化每一步 Stream 的数据变化过程。


十、总结

Stream 的本质,是一种声明式数据处理模型

  • 用“做什么”替代“怎么做”
  • 用函数式思想提升表达力
  • 用链式调用提高可读性

核心心法:

过滤(filter)→ 转换(map)→ 排序(sorted)→ 收集(collect)

掌握这一条主线,你已经掌握了 Stream 的 80% 用法。