《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 做延迟执行、短路优化、合并操作。有副作用会让所有优化失效。

  • 无时序依赖

相关推荐
许彰午8 小时前
14_Java泛型完全指南
java·windows·python
智慧物业老杨8 小时前
司法绿色通道下的物业纠纷数智化解决方案——基于“三优先“机制的全流程技术落地实践
java·django
2601_961194028 小时前
2026初级会计实务公式总结大全|计算题公式手册PDF
java·spring·eclipse·pdf·tomcat·hibernate
做个文艺程序员8 小时前
第1篇:K8s 核心概念精讲:Pod、Deployment、Service 与 Namespace——Java 开发者快速上手指南
java·云原生·容器·kubernetes·容器编排
广州灵眸科技有限公司8 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) Easy-Eai编译环境准备与更新
服务器·前端·人工智能·python·深度学习
TechWayfarer9 小时前
IP风险等级评估接入实战:金融信贷如何用IP画像辅助风控审核
python·tcp/ip·安全·金融
Esaka_Forever9 小时前
uv init 完整用法(Python 最快包管理器)
服务器·python·uv
流星白龙9 小时前
【MySQL高阶】19.变更缓冲区,自适应哈希索引,日志缓冲区
数据库·windows·mysql
ylscode10 小时前
Comodo防火墙曝致命零日漏洞:单个IPv6数据包即可触发Windows蓝屏死机
运维·网络·windows·安全·安全威胁分析
x***r15110 小时前
nvm-windows 安装教程:Node.js 多版本管理(避坑版)
windows·node.js