《Effective Java》解读第46条:优先选择Stream中无副作用的函数

第46条:优先选择Stream中无副作用的函数

在 Java Stream 流水线中,传给中间操作、终端操作的函数,必须是无副作用的------ 函数只做计算 / 映射 / 过滤,不修改外部变量、不读写外部状态、不产生额外行为。

什么是「副作用」?

函数执行时,除了返回一个结果,还偷偷修改了外部世界。

修改外部变量(count++、list.add())

读写文件、网络、数据库

打印日志、控制台输出

修改静态变量、共享对象状态

Stream 设计初衷是声明式、并行安全、可复用、易理解,副作用会彻底破坏这些特性。

例子

反例:

java 复制代码
// 错误示例:有副作用,破坏 Stream 设计
List<String> words = Arrays.asList("apple", "banana", "cat");
int count = 0;

// 错误:lambda 里修改外部 count,产生副作用
words.stream()
     .filter(w -> w.length() > 3)
     .forEach(w -> count++); // 副作用:修改外部变量

System.out.println(count);
  • 修改了外部变量count,当切换为parallelStream()时,会产生并发问题。
  • 副作用在lambda中,难以排查和维护。
  • forEach 应该只应用于最终消费(如打印、存库),不应用于计算。

正例:

java 复制代码
// 正确:无副作用,纯声明式计算
List<String> words = Arrays.asList("apple", "banana", "cat");
long count = words.stream()
                  .filter(w -> w.length() > 3)
                  .count(); // 终端操作直接返回结果,无外部修改

只做输入->输出,不触碰外部变量。

经典使用例子:

java 复制代码
// ❌ 错误(副作用:收集时修改外部 List)
List<String> result = new ArrayList<>();
words.stream()
     .map(String::toUpperCase)
     .forEach(result::add); // 副作用:修改外部list

// ✅ 正确(无副作用,使用收集器)
List<String> result = words.stream()
                           .map(String::toUpperCase)
                           .collect(Collectors.toList());

✅ 允许的行为(无副作用)

纯计算:map(x -> x*2)

纯判断:filter(x -> x>10)

纯提取:map(Student::getName)

使用标准收集器:collect(toList())、count()、sum()

groupingBy 可以分组 + 下游收集器,实现分组统计

counting () 只能放分组里,不要直接用

求和、平均、计数优先用 stream 原生方法,不要用 collect

groupingBy 有 3 个版本,可指定 Map 类型

partitioningBy 分成 true/false

parallel 用 groupingByConcurrent

❌ 禁止的行为(有副作用)

lambda 里修改外部变量、集合

lambda 里 IO 操作(打印、读写文件)

lambda 里修改对象内部状态

用 forEach 做计算 / 收集

为什么最好选择无副作用方法?

  • 并行安全

    Stream 可以轻松并行,但有副作用的函数在并行下一定会出错(竞态条件、数据丢失)。无副作用函数天然线程安全。

  • 可读性强

    声明式代码:我要什么,而不是我要怎么做。别人一眼看懂:过滤→映射→收集,没有隐藏逻辑。

  • 可优化、可复用

    JVM 可以对无副作用的 Stream 做延迟执行、短路优化、合并操作。有副作用会让所有优化失效。

  • 无时序依赖

相关推荐
徽先生2 小时前
注释标准模板
python
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第十一期 - 模板方法模式】模板方法模式 —— 流程骨架与钩子实现、优缺点与适用场景
java·后端·设计模式·软件工程·模板方法模式
gf13211112 小时前
流光剪辑_调用生成图片模型/apimart调用生成视频模型
python
九皇叔叔2 小时前
005-SpringSecurity-Demo 配置外部文件映射
java·springboot·文件·springsecurity
Gent_倪2 小时前
Quartz 入门指南(二)Spring Boot + Quartz 示例
java·spring boot·quartz
唐不是营养物质2 小时前
无头浏览器chromedriver使用(目前不支持国产操作系统)
java·pdf
chenglin0162 小时前
Semantic Kernel 内核详解
后端·python·flask
B站_计算机毕业设计之家2 小时前
计算机毕业设计:Python城市地铁网络可视化分析系统 Flask框架 数据分析 可视化 高德地图 数据挖掘 机器学习 爬虫(建议收藏)✅
网络·python·信息可视化·数据挖掘·flask·课程设计·美食
源码之家2 小时前
计算机毕业设计:Python地铁数据可视化分析系统 Flask框架 数据分析 可视化 高德地图 数据挖掘 机器学习 爬虫(建议收藏)✅
大数据·python·信息可视化·数据挖掘·flask·汽车·课程设计