3

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

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

4

3 回答 3

2

根据文档

当两个对象的哈希值相同且两个对象是 eql? 对彼此。

改变一个键不会改变它存储的哈希值。改变键后,尝试使用[1,2]匹配hash但不匹配的索引eql?,而[1,2,3]匹配eql?但未找到的索引hash

请参阅这篇文章以获得更详细的解释。

但是,您可以 rehashtest以根据当前键值重新计算散列:

test.rehash
test[[1,2,3]] # => 12
于 2013-05-28T22:26:17.090 回答
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方法。当对象作为key时,Hash调用该方法,并使用得到的数字来存储和检索key。(有处理重复数字(哈希冲突)的智能程序)。检索是这样的:

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

这将重新计算所有哈希数。

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

于 2013-05-28T22:42:48.713 回答
1

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

因此,在 do 之后, or不再返回存储的值key << 3是有意义的,因为在分配给was 时。test[key]test[[1, 2, 3]]keytest[1, 2]

棘手的是,它test[[1, 2]]也会返回nil。这就是 Ruby 的局限。

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

test.rehash
test[key]          # => 12
test[[1,2]]        # => nil
test[[1,2,3]]      # => 12
于 2013-05-28T22:33:16.140 回答