克隆数组及其内容

31
我想复制一个数组,在副本中进行原地修改,而不影响原始数组。这段代码失败了。
a = [
  '462664',
  '669722',
  '297288',
  '796928',
  '584497',
  '357431'
]
b = a.clone
b.object_id == a.object_id # => false
a[1][2] = 'X'
a[1] #66X722
b[1] #66X722

复制品应该与对象不同。为什么它的行为就像是一个引用一样?


如果您使用检查方法,它应该显示分配的内存不同的两个不同对象。克隆会复制变量,但不会复制它们所引用的对象。 - bkunzi01
6个回答

26

你可以使用map在每个数组元素上调用clone,而不是在数组本身上调用clone

b = a.map(&:clone)

在问题中提到的示例中,这样做是有效的,因为你会得到数组中每个元素的新实例。


1
这仍然不是深拷贝。例如:a = [[1, 2, [3, 4]]]; b = a.map(&:clone); a[0][2][0] = 'foo' 将会同时改变 b - Sundeep
1
@Sundeep 这个问题是关于一个简单的对象数组,所以不需要进行深拷贝。如果您想更详细地讨论,请将您的新示例作为一个单独的问题发布到Stack Overflow上。 - wjordan
1
是的,针对这个问题的示例,这个方法是可行的。我应该将我的评论标注为"供参考/备注"。我并不意味着答案是错误的。 - Sundeep
1
实际上,如果您使用 map(&:clone) 方法而不是问题中发布的 clone 方法,则可以获得 OP 所寻找的结果。因此,这确实是一个有效的答案。 - pjvleeuwen

25

你需要对数组进行深拷贝。

以下是实现方法:

Marshal.load(Marshal.dump(a))

这是因为您正在克隆数组而不是内部的元素。因此,数组对象是不同的,但它包含的元素是相同实例。例如,在您的情况下,您也可以使用a.each{|e| b << e.dup}


谢谢,我会用的,但是我认为只有符号引用相同的对象,为什么字符串a[1]和b[1]引用相同的对象? - Redouane Red
不错!!!我已经尝试了dup,clone和Array.new(a),但仍然改变了它,我认为Ruby创建了一个具有每个值指针的数组。 - Horacio
在 Ruby 中,一切都是对象,因此数组包含对字符串的引用而不是字符串本身。当您克隆数组时,引用会被复制,但副本仍然指向原始字符串。 - Nic Nilov
@RedouaneRed 字符串 a[1] b[1] 相同的对象。我不确定我理解您对符号的担忧,因为符号是不可变的。 - jazzytomato
我只是想说,当你创建两个相似的字符串时,你会创建两个对象,但是当你创建两个相似的符号时,你只会创建一个。感谢@NicNilov的解释。 - Redouane Red
在这里,序列化和反序列化是不必要的,当数组中的对象无法转换为字符串时也没有用处。我怀疑这会增加额外的开销。这个答案 (a.map(&:clone)) 是 OP 特定问题的最佳解决方案,是 a.each{|e| b << e.dup} 更符合惯用法的版本。 - ggorlen

10
您可以使用 #dup 创建对象的浅层副本,这意味着“对象的实例变量被复制,但它们引用的对象并未被复制”。 例如:
a = [1, 2, 3]

b = a.dup

b # => [1, 2, 3]

来源:https://ruby-doc.org/core-2.5.3/Object.html#method-i-dup

编辑:请听下面Paul的解释,我误解了问题。

这段内容是关于Ruby编程语言中Object类中的dup方法的说明。

1
对于这个问题,dupclone 的结果与 OP 获得的完全相同,因此并不是这个问题的答案。对于那些现在对 dupclone 之间的区别感到好奇的人,请参见 https://dev59.com/FGkw5IYBdhLWcg3wBmGA. - pjvleeuwen

7

试试这个:

b = [] #create a new array 
b.replace(a) #replace the content of array b with the content from array a

此时这两个数组是指向不同的对象,但内容相同。


2
这也不能进行深拷贝...尝试使用 a = [[1, 2, [3, 4]]] - Sundeep
Sundeep说:如果你使用“replace”方法而不是问题中所发布的“clone”方法,你将得到完全相同的结果。因此,这只是与OP所做的相同事情的另一种方式(尽管他显然正在寻找不同的结果)。 - pjvleeuwen

4

您可以直接映射数组元素,我相信这是目前最干净的解决方案。

array.map(&:itself)


-1

不确定这个问题是否在其他地方得到了解答。尝试搜索但没有成功。

试试这个

current_array =["a", "b","c","d"]
new_array = current_array[0 .. current_array.length]
new_array[0] ="cool"

output of new_array
 "cool","b","c","d"

希望这可以帮助到你。

解决方案可行。不确定是否已有人回答过这个问题,因为在搜索后未能找到解决方案,不想重复回答。希望这可以帮到你。 - Anutosh
你在这里错过了一个技巧。你需要更改其中一个数组并检查此更改是否反映到另一个数组中。 - stema

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接