复用Map初始化对象时,对象变更同步引发Map异常变更的技术问题
问题原因与解决方案
这问题我之前在项目里也踩过坑!核心问题出在引用类型的传递机制上,咱们来拆解下你的代码到底发生了什么:
为什么修改Obj的List会同步改变Map的List?
Groovy里用map as Obj进行类型转换时,对于像List这种引用类型,并不会自动创建新的实例——它只是把Map里list对象的内存引用直接赋值给了Obj的list属性。也就是说,obj.list和map.list指向的是同一个ArrayList对象!不管你通过哪个变量去修改这个List,另一个变量看到的结果都会同步变化。
你的循环里每次调用obj.list.add(iterator),其实都是在给同一个List实例添加元素,所以最后Map的List和Obj的List内容完全一致,也就不奇怪了。
解决办法
有几种简单的方式可以避免这个问题,根据你的场景选就行:
1. 手动创建新List并复制原内容(最直接)
每次实例化Obj的时候,主动给list属性创建一个新的ArrayList,并把Map里的List内容复制进去:
def map = [ id: 0, list: [1,2] ] class Obj { int id = 0 List<Integer> list = new ArrayList<>() } for (iterator in 1..10){ map.id = iterator // 手动初始化Obj的属性,给list创建新实例 Obj obj = new Obj( id: map.id, list: new ArrayList<>(map.list) ) obj.list.add(iterator) println "Obj.List: ${obj.list.toString()} Obj.id: ${obj.id}" println "map: ${map.list.toString()}" }
2. 给Obj添加自定义构造方法(更优雅)
如果需要频繁从Map转换为Obj,可以给Obj加一个构造方法,自动处理引用类型的复制:
def map = [ id: 0, list: [1,2] ] class Obj { int id = 0 List<Integer> list = new ArrayList<>() // 接收Map参数的构造方法,自动复制List Obj(Map map) { this.id = map.id // 复制原List,空值时默认空List this.list = new ArrayList<>(map.list ?: []) } } for (iterator in 1..10){ map.id = iterator Obj obj = new Obj(map) obj.list.add(iterator) println "Obj.List: ${obj.list.toString()} Obj.id: ${obj.id}" println "map: ${map.list.toString()}" }
3. 使用克隆(适合临时场景)
利用ArrayList的clone()方法创建新实例(注意:这是浅克隆,适合元素是不可变类型如Integer的场景):
def map = [ id: 0, list: [1,2] ] class Obj { int id = 0 List<Integer> list = new ArrayList<>() } for (iterator in 1..10){ map.id = iterator Obj obj = map as Obj // 克隆出一个新的List实例,切断引用 obj.list = obj.list.clone() obj.list.add(iterator) println "Obj.List: ${obj.list.toString()} Obj.id: ${obj.id}" println "map: ${map.list.toString()}" }
修改后的预期输出
最后一次迭代的输出会变成:
Obj.List: [1, 2, 10] Obj.id: 10
map: [1, 2]
这样Obj的修改就不会影响原Map的内容了!
内容的提问来源于stack exchange,提问作者Jacob Duncan
相关产品推荐
相关产品推荐

