数组元素修改行为错误

Array element modification behaves wrong

我有一个填充有默认字符串的数组,我试图在随机位置替换默认字符串中的部分字符。

如果我这样做,我将更改数组中的所有元素:

arr = ["*"] * 10
arr[0][0..2] = "aaa"
arr 
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"]

但是如果我以不同的方式初始化数组,它会起作用:

(0..10).each.map {|i| arr[i] = "*"}
arr[0][0..2] = "aaa"
arr
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*"]

更多,初始化和所有元素相同:

str = "*"
(0..10).each.map {|i| arr[i] = str}
arr[0][0..2] = "aaa"
arr
# => ["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"]

相反,我这样做是为了用独特的元素初始化它:

str = "*"
(0..10).each.map {|i| arr[i] = "#{str}" }
arr[0][0..2] = "aaa"
arr
# => ["aaa", "*", "*", "*", "*", "*", "*", "*", "*", "*"]

这种行为的背景是什么?

当您执行 arr=["*"]*10 时,您将完全相同的 String 对象放入所有数组槽中。然而,(0..10).each.map { |i| arr[i] = "*" } 正在为数组中的每个元素创建一个新的 String 对象。

用以下代码说明:

(0..10).each.map { |i| arr[i] = "*" }
arr[0].equal? arr[1] # Check if first and second elements point to same Object
# => false
arr = ["*"] * 10
arr[0].equal? arr[1]
# => true

数组存储对对象的引用。当你以这种方式初始化一个数组时,你会得到一个包含十个对同一个字符串的引用的数组。然后你修改字符串。

arr = ['*']*3
# => ["*", "*", "*"]
arr.map &:object_id
# => [70305424624600, 70305424624600, 70305424624600]

相比之下,这种方式 ruby 为每个元素分配一个新字符串:

Array.new(3){ '*' }.map &:object_id
# => [70184497001120, 70184497001060, 70184497001000]
arr = ["*"]*10

此语句创建一个包含 10 个元素的数组,但所有这些元素都不是唯一的,并且引用您在填充数组之前创建的字符串 "*"" 的同一个实例。我的意思是你的代码是一样的:

a = "*"
arr = [a, a, a, a, a, a, a, a, a, a]
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"]

arr.map(&:object_id)
#=> [15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420, 15424420]

数组的所有元素都引用同一个字符串实例,因此当您更改数组中的任何元素值时,您实际上会更改 a 变量的值,因此作为结果你会得到相同的数组,其中填充了 a 变量,但是由于它的值被更改为 "aaa" 那么输出看起来像:

["aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"]

在第二个示例中,您使用 "*" 字符串的新实例填充数组的每个元素,因此当您更改单个数组值时,它只会影响这个具体元素array 和所有其他的都是一样的,因为它们指的是内存中分配的不同对象。

arr = (0..10).each.map { |i| arr[i] = "*" }
#=> ["*", "*", "*", "*", "*", "*", "*", "*", "*", "*"]
arr.map(&:object_id)
#=> [14451520, 14451500, 14451480, 14451440, 14451420, 14451320, 14451300, 14451280, 14451260, 14451240, 14451160]