Lambda配合forEach可就地修改可变对象属性,适用于状态更新;map则生成新集合,适合需保留原数据的转换场景,二者均需避免并发修改与副作用。

直接用 Lambda 配合集合的 forEach 或 stream().map() 就能批量改属性,不用写冗长的 for 循环,代码更短、意图更清楚、维护成本更低。
用 forEach 直接修改原集合对象
适用于需要就地更新对象字段(比如学生年龄+10、订单状态设为已发货)的场景。前提是集合元素是可变对象,且你允许修改原对象。
- 语法简洁: list.forEach(item -> item.setField(newValue));
- 注意不能在 forEach 中增删 list 元素,否则抛 ConcurrentModificationException
- 不返回新集合,操作后原 list 已变更,适合“改状态”类操作
用 stream().map() 生成新集合
适用于需要保留原始数据、同时产出转换后结果的情况,比如把所有用户名转大写、给每个商品加折扣价。
- 链式调用清晰: List
result = list.stream().map(x -> new NewType(x)).collect(Collectors.toList()); - map 内部应返回新对象或副本,避免副作用;原 list 不受影响
- 配合 filter / sorted 可组合复杂逻辑,比如“筛选出有效用户 → 转成 DTO → 按注册时间排序”
Java 和 C# 的关键区别提醒
Java 的 forEach 是 Consumer,只执行不返回;C# 的 ForEach 是就地修改,而 Select 才对应 Java 的 map —— 别混淆语义。
- C# 中 list.ForEach(x => x.Title = "测试:" + x.Title); 是合法且常用
- Java 中 list.forEach(x -> x.setTitle("测试:" + x.getTitle())); 同样有效,但需确保 list 不被并发修改
- 两者都不建议在 lambda 里修改外部变量或引发 IO/网络调用,保持函数轻量
避开常见坑的实操建议
不是所有场景都适合 Lambda 批量改值,选错方式反而增加风险。
- 如果集合是 Arrays.asList() 包装的固定数组,set() 会报 UnsupportedOperationException —— 改用 new ArrayList(xxx)
- 修改基本类型包装类(如 Integer)字段?不行,它们不可变 —— 必须换用自定义对象或改用传统 for 索引赋值
- 涉及数据库主键、时间戳等敏感字段时,建议显式加注释说明“此处批量更新”,方便后续审计

















