Groovy列表迭代与Map更新异常:仅保留最后元素值问题
问题分析与解决方案
问题根源
这是因为Groovy(和Java一致)中可变对象按引用传递,你的代码只做了浅拷贝,导致多个phase共享了同一个可变对象引用:
1. 为什么id字段更新正常
phase.id = id是给每个独立的phaseMap新增键值对,每个phase都是单独的Map实例,彼此互不干扰。- 模板中的
type是字符串,属于不可变类型,赋值时传递的是引用,但由于字符串不可修改,所以每个phase的type都能保持正确值。
2. 为什么directories字段更新异常
template.directories是列表,列表内的元素是Map,二者都是可变对象。你通过phase.put(it, template.get(it))复制时,只是把template.directories的引用赋值给了phase.directories——所有phase的directories都指向同一个列表,列表内的Map也是同一个实例。- 每次迭代修改
phase.directories[0].put('command', ...)时,都是在修改同一个底层Map,最后一次迭代(id=3)的修改会覆盖之前所有操作,导致所有phase的directories都显示最后一个值。
解决方案
需要对模板中的可变对象做深拷贝,确保每个phase拥有独立的可变对象实例,以下是几种可行方法:
方法一:手动深拷贝可变对象
针对模板中的可变字段单独处理,复制列表及内部的Map:
def template = [type: 'a', directories: [['dir': 'a/b/c']]] def ids = [1,2,3] def allPhases = [] ids.each { id -> def phase = [:] // 复制不可变类型字段 phase.type = template.type // 深拷贝directories列表及内部的Map phase.directories = template.directories.collect { it.clone() } phase.id = id phase.directories[0].put('command', "apply/${id}") allPhases.add(phase) } println(allPhases)
方法二:使用Groovy内置的deepCopy方法(Groovy 2.5+)
直接对整个模板做深拷贝,自动处理所有嵌套的可变对象:
def template = [type: 'a', directories: [['dir': 'a/b/c']]] def ids = [1,2,3] def allPhases = [] ids.each { id -> // 深拷贝生成独立的模板实例 def phase = template.deepCopy() phase.id = id phase.directories[0].put('command', "apply/${id}") allPhases.add(phase) } println(allPhases)
方法三:结合Map的clone与列表拷贝
先克隆模板Map,再单独拷贝可变的directories字段:
def template = [type: 'a', directories: [['dir': 'a/b/c']]] def ids = [1,2,3] def allPhases = [] ids.each { id -> def phase = template.clone() // 重新拷贝directories,避免引用共享 phase.directories = phase.directories.collect { it.clone() } phase.id = id phase.directories[0].put('command', "apply/${id}") allPhases.add(phase) } println(allPhases)
验证输出
执行上述代码后,每个phase的directories都会对应各自的id值:
[ [type:a, directories:[[dir:a/b/c, command:apply/1]], id:1], [type:a, directories:[[dir:a/b/c, command:apply/2]], id:2], [type:a, directories:[[dir:a/b/c, command:apply/3]], id:3] ]
内容的提问来源于stack exchange,提问作者TomNash
相关产品推荐
相关产品推荐

