CodeGym /课程 /JAVA 25 SELF /Stream API 入门:为什么需要流

Stream API 入门:为什么需要流

JAVA 25 SELF
第 30 级 , 课程 0
可用

1. 命令式方法的问题

先从一个经典问题开始。假设我们有一个用户列表:

List<User> users = ...; // 已填充

假设我们需要获取所有已成年的用户(18 岁及以上)的姓名列表。过去我们会怎么做?

List<String> names = new ArrayList<>();
for (User user : users) {
    if (user.getAge() >= 18) {
        names.add(user.getName());
    }
}

乍看之下很简单,但请注意这些“技术性”步骤:声明一个结果列表、遍历所有元素、检查条件、把结果加入列表。

如果需求稍微复杂一点——例如,要获取按字母顺序排序的、所有用户名以字母“A”开头的用户的唯一 e‑mail 列表——代码会很快膨胀:过滤、提取字段、去重、排序,甚至还可能要规范化 e‑mail 格式,等等。结果原本简短明晰的逻辑变成一大堆样板代码,既难读又难维护。

这种做法的缺点可以概括为三点:大量重复代码、容易出错(比如忘了排序或错误地处理了重复项),以及难以组合操作。这就是所谓的 命令式风格:你一步步告诉计算机,如何执行任务,而不是你想要得到什么。

2. Stream API —— 声明式风格

随着 Java 8 的发布,出现了 Stream API——一个让你以“做什么”而非“怎么做”的方式来处理集合的工具。这种方式称为 声明式

什么是 Stream?
它不是一个独立的集合,而更像是数据流:元素序列依次通过一串操作——过滤、转换、排序、收集结果等。流本身不存储数据,只是把数据“输送”过处理管线。各类操作几乎可以像乐高积木一样自由组合:把链条拼好然后运行即可。

示例:

List<String> names = users.stream()                // 从用户列表创建流
    .filter(u -> u.getAge() >= 18)                 // 仅保留年龄≥18 的用户 
    .map(User::getName)                            // 将 User → String(取姓名)
    .collect(Collectors.toList());                 // 把结果收集为列表

就是这样——用一条链式表达就把任务描述清楚了:“取用户、按年龄过滤、取姓名、收集为列表”。无需手写循环、创建临时变量,也不用担心遗漏步骤。

Stream API 像工厂流水线一样工作:你定义处理的各个阶段,数据会自己流经这些阶段。简洁、美观,而且可读性大幅提升。

3. Stream API 的优势

  • 简洁与可读性。 把同一任务用命令式和声明式各实现一遍,你会立刻看出哪种更易读、更易维护。
  • 易于组合操作。 操作可以轻松“拼接”成链:filtermapsortedcollect——全部都在同一条连贯的流水线上完成。
  • 更少出错。 Stream API 帮你摆脱样板活:无需手动管理中间集合和循环——减少漏步骤或下标错误的机会。
  • 支持并行处理。 只需把 parallelStream() 替换 stream(),就能轻松把处理扩展到多核。细节——稍后介绍。
  • 现代编程风格。 函数式编程的理念(函数组合、无显式循环)提升表达力,也更受市场青睐。

Stream API 的简史

Stream API 于 Java 8(2014 年)引入,成为该语言的一次质的飞跃。在此之前,稍微复杂些的集合操作都要写大量辅助代码,而其他平台早已提供了声明式方式(mapfilterreduce 等)。

自那以后,Stream API 已成为 Java 进行集合处理的事实标准。想写出现代化的 Java 代码——离不开它。

4. Stream API 的适用场景

  • 过滤: 只保留需要的元素(例如,所有大于 18 岁的用户)。
  • 转换: 提取字段或创建新对象(例如,用户姓名列表)。
  • 聚合: 计算总和、平均值、数量、最大/最小值。
  • 排序: 按需要的键对元素排序。
  • 分组: 按类别对元素划分。
  • 收集结果: 收集为列表、集合、映射、字符串等。

示例任务:

  • 获取所有姓名以字母“A”开头的用户的 e‑mail 列表。
  • 计算用户的平均年龄。
  • 查找第一个具有特定 e‑mail 的用户。
  • 把所有不重复的居住城市收集到一个集合中。

5. 实用细节

流是如何工作的

[User1] --\
[User2] ---|--> [ filter ] --> [ map ] --> [ collect ] --> List<String>
[User3] --/

1. filter——仅保留满足条件的用户。
2. map——把用户转换为某个字段,例如 e‑mail。
3. collect——把 e‑mail 收集到所需的集合中。

语法:如何创建流

  • 来自集合:list.stream() —— 集合元素的流。
  • 来自数组:Arrays.stream(array)
  • 来自若干值:Stream.of("a", "b", "c")

6. 迁移到 Stream API 时的常见错误

错误 1:在流内部修改集合。 Stream API 并不用于修改源集合(例如,不要在 forEach 的函数体里调用 list.remove())。需要删除元素时,可在处理前后直接对集合使用 removeIf 等方法。

错误 2:混淆集合与流。 流不是集合!流只会“走过”元素一次,随后即关闭。若需再次处理数据——请创建一个新的 stream()

错误 3:链式调用过于复杂。 不要把表达式变成由十几步组成的“怪物”。如果链条变得很长——请拆成若干步骤,并使用中间变量以提升可读性和调试体验。

评论 (2)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
ncksllpo 级别 44,Cherkasy,Ukraine
24 二月 2026
forEach()
ncksllpo 级别 44,Cherkasy,Ukraine
24 二月 2026
😩日常超纲