更改后的数组键在哈希表中的行为

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

为什么会这样工作呢?为什么我不能提供一个键来返回与我最初用作键的列表相关联的值呢?

不同形式的相同根本问题:https://dev59.com/eWPVa4cB1Zd3GeqP3jrY - mu is too short
3个回答

2
根据文档:
两个对象的哈希值相同且这两个对象是eql?相等的时候,它们指向同一个哈希键。
更改键不会更改其存储在其中的哈希值。更改键后,尝试使用[1,2]进行索引将匹配hash但不匹配eql?,而[1,2,3]匹配eql?但无法被hash找到。
有关更详细的解释,请参见此文章
您可以重新计算基于当前键值的哈希值来重新哈希test
test.rehash
test[[1,2,3]] # => 12

基本上,Ruby允许您使用内置组件自我毁灭。不知道重新散列功能! - Pyrce

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方法。当对象被用作键时,哈希表会调用此方法,并使用生成的数字来存储和检索键。(有智能程序来处理哈希冲突)。检索过程如下:

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

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

注意:对于字符串键,使用副本计算哈希编号,因此修改原始键不会产生影响。


为了补充这个答案,关键是hash方法依赖于对象的内容而不是标识。因此,具有相同内容的不同对象将具有相同的hash结果。hashobject_id是不同的。 - sawa
我没有理解这句话的意思:“对于字符串键,计算哈希值时使用的是副本,因此修改原始键不会有影响。” 你能举个例子详细说明一下吗?a = "abc" h = {a=>12} p h[a] #=> 12 a << "d" p a #=> "abcd" p h[a] #=> nil - Arup Rakshit
@steenslag 现在有意义了,但是Hash#rehash在两种情况下都需要。感谢您的注释,我之前不知道。但是是的,键作为字符串符合您的注释。您有任何关于这种实现的想法吗? - Arup Rakshit

1
如果一个数组的身份作为哈希键很重要,那将会很不方便。如果你有一个键为[1, 2]的哈希表,你想要通过另一个具有相同内容的数组对象[1, 2]来访问它。你想要通过内容访问,而不是身份。这意味着存储在哈希表中作为键的特定对象(具有特定对象ID)对于哈希表并不重要,重要的是在分配给哈希表时键的内容。

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

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

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

test.rehash
test[key]          # => 12
test[[1,2]]        # => nil
test[[1,2,3]]      # => 12

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