Ruby中<<、push与+=数组操作结果不同的原因咨询
问题根源:共享数组实例导致的意外修改
你遇到的问题核心在于初始化result哈希时,所有键对应的空数组其实是同一个对象,而+=和<</push在Ruby中的行为本质不同,才导致了两种完全不同的结果。
1. 为什么初始化会出问题?
看你初始化result的代码:
result = keys.product([[]]).to_h
这里的[]是一个单独的空数组实例,product方法会把这个同一个数组和每个键配对,最终result里的所有键,指向的都是同一个数组对象!比如result["1"].object_id和result["2"].object_id是完全相同的。
2. += vs <</push的本质区别
用+=时的行为
Ruby中result[k] += [v]等价于:
result[k] = result[k] + [v]
+运算符会创建一个新的数组(把原数组和[v]拼接),然后把这个新数组赋值给result[k]。也就是说:
- 第一次执行时,
result[k]原本是共享数组,但赋值后就变成了独立的新数组; - 后续每次
+=都是在当前键的独立数组上拼接,所以每个键的数组会正常收集对应的值。
用<</push时的行为
<<和push都是在原数组上直接修改(原地操作,不会创建新数组)。因为所有键一开始共享同一个数组,所以你每次执行result[k] << v,都是在修改那个唯一的共享数组。比如:
- 遍历第一个Hash的10个键时,每个键的
<< v都是往同一个数组里加10,最后这个数组里有10个10; - 遍历第二个Hash的10个键时,又往同一个数组里加10次100,数组变成20个元素;
- 以此类推,最终所有键指向的都是这个被反复添加的大数组,就出现了你看到的重复元素。
3. 修正方案:给每个键分配独立的空数组
只需要修改result的初始化代码,确保每个键对应的都是新的空数组实例即可:
# Ruby 2.1+ 推荐写法 result = keys.to_h { |k| [k, []] } # 兼容旧版本的写法 result = Hash[keys.map { |k| [k, []] }]
这样每个键的数组都是独立的对象,之后用<<或者push就能得到和+=一样的预期结果了。
内容的提问来源于stack exchange,提问作者MDickten
相关产品推荐
相关产品推荐

