散列中更改的数组键的行为

时间:2013-05-28 21:58:01

标签: ruby hash

Ruby允许将一个可变对象用作哈希键,我很好奇当对象更新时它是如何工作的。如果引用的对象在更新时无法从关键请求中恢复,那么它似乎是无法恢复的。

key = [1,2]
test = {key => 12}

test               # => {[1, 2] => 12}
test[key]          # => 12
test[[1,2]]        # => 12
test[[1,2,3]]      # => nil

key << 3

test               # => {[1, 2, 3] => 12}
test[key]          # => nil
test[[1,2]]        # => nil
test[[1,2,3]]      # => nil

为什么这样做?为什么我不能为哈希提供一个键,它将返回与我原来用作键的列表相关的值?

3 个答案:

答案 0 :(得分:2)

根据the documentation

  

当两个对象的哈希值相同且两个对象是eql时,它们引用相同的哈希键?彼此。

变换键不会改变它存储的哈希值。变异后,尝试使用[1,2]进行索引与hash匹配但不匹配eql?,而[1,2,3]eql?匹配,但hash找不到test。 {1}}。

请参阅this article以获得更详细的解释。

但是,您可以重新计算test.rehash test[[1,2,3]] # => 12 ,以根据当前键值重新计算哈希值:

{{1}}

答案 1 :(得分:2)

class D
end

p D.new.methods.include?(:hash) #=> true
# so the D instance has a hash method. What does it do?
p D.new.hash #=> -332308361 # just some number

(几乎)Ruby中的每个对象都有hash方法。当对象用作键时,Hash调用此方法,并使用结果数来存储和检索键。 (有处理重复数字的智能程序(哈希冲突))。检索是这样的:

a_hash[[1,2,3]]
# the a_hash calls the hash method to the [1,2,3] object
# and checks if it has stored a value for the resulting number.

此编号仅创建一次:将密钥添加到哈希实例时。 在将密钥包含在散列中之后开始弄乱密钥时出现问题:对象的hash方法将与散列中存储的方法不同。

  • 不要这样做,或
  • 考虑不使用可变对象作为键,或
  • 记得要及时做到:

    a_hash.rehash

将重新计算所有哈希值。

注意:对于字符串键,副本用于计算哈希值,因此修改原始键无关紧要。

答案 2 :(得分:1)

如果数组的标识作为散列键很重要,那将是不方便的。如果您使用密钥[1, 2]的哈希,则希望能够使用具有相同内容的其他数组对象[1, 2]来访问该哈希。您希望通过内容进行访问,而不是身份。这意味着将特定对象(具有特定对象id)存储为密钥对于散列无关紧要。重要的是在分配给哈希时密钥的内容。

因此,在执行key << 3后,test[key]test[[1, 2, 3]]不再返回存储的值是有意义的,因为key在分配给{{1}时} test

棘手的是[1, 2]也会返回test[[1, 2]]。这是Ruby的限制。

如果您希望哈希反映在关键对象中所做的更改,则有一个方法nil

Hash#rehash