当前位置: 首页 > news >正文

Stream 过滤后修改元素,却意外修改原列表

1. 问题重现

List<StringBuffer> list = Lists.newArrayList(new StringBuffer("a"),new StringBuffer("b")
);
List<StringBuffer> filterList = list.stream().filter(v -> "a".equalsIgnoreCase(v.toString())).collect(Collectors.toList());for (StringBuffer v : filterList) {v.append("b");
}System.out.println(list); // 输出:[ab, b]

在这里插入图片描述
看似只是想处理过滤结果集,但是最终打印原 list 却变成 [ab, b]。为什么会这样?

2. 原因分析

这是因为引用传递导致原始数据被修改,Java 对象是通过引用在集合中传递的,Stream.filter 和 collect 并不会自动复制对象,只是把满足条件的对象引用留下来形成新的列表。结果导致filter 后的 filterList 和原 list 指向的是同一个 StringBuffer 实例。

3. 正确做法

3.1 方案一:深拷贝元素再修改

如果你只想处理过滤后的数据,避免污染原列表状态,需要对每个元素执行深拷贝(Clone 或构造新实例)。

List<StringBuffer> safeList = list.stream().filter(v -> "a".equalsIgnoreCase(v.toString())).map(v -> new StringBuffer(v.toString())).collect(Collectors.toList());for (StringBuffer v : safeList) {v.append("b");
}
System.out.println(list);      // [a, b]
System.out.println(safeList);  // [ab]
3.2 方案二:手动 new 集合再操作
List<StringBuffer> safeList = new ArrayList<>();
for (StringBuffer sb : list) {if ("a".equalsIgnoreCase(sb.toString())) {safeList.add(new StringBuffer(sb.toString()));}
}
3.3 方案三:如果只是想批量修改原列表元素

如果业务是要修改原列表的对象状态,用 forEach 或 replaceAll 更清晰。

//  forEach 
list.replaceAll(sb -> {if ("a".equalsIgnoreCase(sb.toString())) {sb.append("b");}return sb;
});// replaceAll
list.stream().filter(v -> "a".equalsIgnoreCase(v.toString())).forEach(v -> v.append("b"));

4. 补充

Stream 的行为应尽量无副作用(no side-effects),peek 操作主要用于调试而非修改状态 。
虽然技术上可以用 .peek(u -> u.setXxx(…)) 修改对象,但这违反函数式编程设计原则,应尽量避免。

http://www.lryc.cn/news/609283.html

相关文章:

  • 人工智能之数学基础:几何型(连续型)随机事件概率
  • Java开发中敏感信息加密存储全解析:筑牢数据安全防线
  • SpringBoot之整合MyBatisPlus
  • linux火焰图
  • javaweb开发之Servlet笔记
  • 大模型中的Token和Tokenizer:核心概念解析
  • 业务系统跳转Nacos免登录方案实践
  • 电力电子技术知识总结-----PWM知识点
  • 【MybatisPlus】join关联查询MPJLambdaWrapper
  • Javaweb————Windows11系统和idea2023旗舰版手动配置Tomcat9全流程解析
  • 性能测试工具ApacheBench、Jmeter
  • ospf笔记和 综合实验册
  • 在Ansys Mechanical中对磨损进行建模
  • 重生之我在10天内卷赢C++ - DAY 10
  • 分布式文件系统05-生产级中间件的Java网络通信技术深度优化
  • STM32F103_Bootloader程序开发13 - 巧用逆向拷贝,实现固件更新的“准原子”操作,无惧升级中的意外掉电
  • Ethereum: 了解炙手可热 Layer 2 解决方案 Base
  • Spring AOP_2
  • Python 小数据池(Small Object Pool)详解
  • NX969NX972美光固态闪存NX975NX977
  • 深度学习中的三种Embedding技术详解
  • Maven - 依赖的生命周期详解
  • MySQL深度理解-MySQL锁机制
  • vllm0.8.5:思维链(Chain-of-Thought, CoT)微调模型的输出结果包括</think>,提供一种关闭思考过程的方法
  • Remix框架:高性能React全栈开发实战
  • 音视频学习(四十九):音频有损压缩
  • 数据结构-双链表
  • 网络通信与Socket套接字详解
  • Flink程序关键一步:触发环境执行
  • 13-day10生成式任务