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

  • 无时序依赖

相关推荐
贫民窟的勇敢爷们19 小时前
SpringBoot整合AOP切面编程实战,实现日志统一记录+接口权限校验
java·spring boot·spring
曦月逸霜19 小时前
啥是RAG 它能干什么?
人工智能·python·机器学习
2301_7693406720 小时前
如何在 Vuetify 中可靠捕获 Chip 关闭事件(包括键盘触发).txt
jvm·数据库·python
AC赳赳老秦20 小时前
供应链专员提效:OpenClaw自动跟踪物流信息、更新库存数据,异常自动提醒
java·大数据·服务器·数据库·人工智能·自动化·openclaw
迈巴赫车主20 小时前
Java基础:list、set、map一遍过
java·开发语言
灵犀学长20 小时前
基于 Spring ThreadPoolTaskScheduler + CronTrigger 实现的动态定时任务调度系统
java·数据库·spring
南 阳21 小时前
Python从入门到精通day66
开发语言·python
为何创造硅基生物21 小时前
嵌入式 LVGL / SquareLine UI 标准命名规则(行业通用版)
windows·ui
m0_5967490921 小时前
JavaScript中手动实现一个new操作符的底层逻辑
jvm·数据库·python
DTAS尺寸公差分析软件21 小时前
DTAS3D v13.0 三维尺寸公差分析软件可申请试用
python·尺寸公差分析·三维公差分析·公差仿真软件·尺寸链计算