Java遍历List修改SDR对象属性 循环结束后值全被覆盖
ArrayList循环修改元素属性后值全被最后一次赋值覆盖
问题复现
业务逻辑为遍历List<SDR>集合,为每个元素计算并设置detail1属性,循环内打印赋值结果符合预期(分别为12、13、14),循环结束后再次遍历打印,所有元素的detail1全部变为最后一次赋值的14。
相关代码如下:
addDetails方法实现:
private List<SDR> addDetails(List<SDR> list) { for(SDR item: list) { xyz = calculateValue(item); // 根据每个item返回不同计算值 item.setDetail1(xyz); System.out.println("getDetail1 before -> " + item.getDetail1()); } System.out.println(); for(SDR item: list) { System.out.println("getDetail1 after -> " + item.getDetail1()); } }
运行输出:
getDetail1 before -> 12 getDetail1 before -> 13 getDetail1 before -> 14 getDetail1 after -> 14 getDetail1 after -> 14 getDetail1 after -> 14
SDR类定义:
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class SDR { private String id; private String detail1; }
calculateValue方法逻辑:根据传入item的id匹配数据库对应记录,返回对应的detail1字符串。
根因
和循环赋值逻辑、calculateValue方法无关,问题出在传入addDetails方法的List本身:List中存储的所有元素引用,都指向堆内存中同一个SDR对象实例。
- 第一次循环中打印的不同值,只是因为每次循环都在修改这唯一的一个SDR对象的
detail1属性,打印的是修改当下的瞬时值 - 循环结束后,这个SDR对象的
detail1属性停留在最后一次赋值的14,此时再遍历List,无论取哪个索引位置的元素,本质都是拿到同一个对象的引用,读取到的属性值自然全是14
可以加一行代码验证:在两个循环内部都打印对象的唯一标识
System.out.println("对象唯一标识:" + System.identityHashCode(item));
运行后会发现所有item打印出的标识完全一致,即可证实List中所有元素都是同一个对象。
常见错误场景
这类问题基本都是构造List时的写法错误导致,典型错误写法:
// 错误:只实例化了一次SDR对象,循环中反复修改同一个对象属性再加入List List<SDR> list = new ArrayList<>(); SDR sdr = new SDR(); for (int i = 0; i < 3; i++) { sdr.setId(String.valueOf(i)); list.add(sdr); }
上述代码生成的List看似有3个元素,实际存储的是同一个SDR对象的3个引用,修改任意一个"元素"的属性,所有位置读取到的值都会同步变化。
如果使用@Builder构造对象,复用同一个Builder实例反复调用build()方法生成元素加入List,也会触发同样的问题。
修复方案
构造List元素时,保证每次添加元素都实例化新的SDR对象,让List中存储不同对象的独立引用即可:
// 正确:每次循环都创建新的SDR实例 List<SDR> list = new ArrayList<>(); for (int i = 0; i < 3; i++) { SDR sdr = new SDR(); sdr.setId(String.valueOf(i)); list.add(sdr); }
如果使用Builder构造,每次生成元素都要重新走Builder创建流程,不要复用同一个Builder实例。
内容的提问来源于stack exchange,提问作者T.P.
相关产品推荐
相关产品推荐

