数组元素修改行为错误

时间:2016-03-31 17:00:56

标签: arrays ruby immutability

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

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

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", "*", "*", "*", "*", "*", "*", "*", "*", "*"]

这种行为的背景是什么?

3 个答案:

答案 0 :(得分:4)

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

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

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

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

答案 1 :(得分:1)

执行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

答案 2 :(得分:0)

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"]

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

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