You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

将HashMap存入自身出现异常行为,恳请技术人士解释原因

为啥把Hash存进自身后会出现反直觉的查找结果?

这个问题的核心在于Ruby中Hash的key是如何被定位的,以及Hash对象自身的hash值会随着内容变化而改变这一特性。咱们一步步拆解你的代码和现象:

先明确两个关键规则

Ruby的Hash在存储和查找键值对时,依赖两个核心方法:

  • hash方法:返回一个整数,用来确定键应该存在哪个“桶”里
  • eql?方法:用来在同一个桶里精确匹配键

而Ruby的Hash对象本身,它的hash值是基于自身内容计算出来的——也就是说,当Hash的内容发生变化时,它的hash值会跟着改变。

逐行分析你的代码

  1. hash = {}
    此时这是一个空Hash,它的hash值是某个固定整数(比如假设是X),对象ID是唯一的。

  2. hash[hash] = hash
    这一步做了两件关键的事:

    • 用当前Hash对象的hashX,把它作为key存入Hash中,对应的值也是它自己
    • 但存入之后,Hash的内容从空变成了有一个键值对,它自身的hash值会重新计算,变成了新的整数Y(和X完全不一样)
  3. hash.keys.first == hash # true
    这是符合预期的,因为keys数组里确实存着那个Hash对象本身——对象ID没变,==(本质调用eql?)自然返回true。

  4. hash[hash] # nil
    问题就出在这:现在你用这个Hash对象去查找时,Ruby会先取它**当前的hashY**去定位桶,但这个键当初是存在X对应的桶里的,自然找不到,返回nil。

  5. hash.key?(hash) # False
    和上面同理,key?方法也是先通过当前的hash值找桶,找不到对应的键,所以返回false。

  6. hash[hash.keys.first] # nil
    虽然hash.keys.first是同一个对象,但此时它的hash值已经是Y了,查找时还是用Y去定位桶,和当初存储时的X不匹配,所以还是找不到。

  7. hash[{}] # nil
    这个很好理解,{}是一个全新的空Hash对象,它的hash值和之前的XY都不一样,当然找不到任何键值对。

直观验证:打印hash值变化

你可以加几行代码亲眼看到变化:

hash = {}
puts "空Hash的hash值: #{hash.hash}"
hash[hash] = hash
puts "存入自身后的hash值: #{hash.hash}"

运行后你会发现这两个hash值完全不同,这就是所有反直觉现象的根源。

总结

简单来说:当你把Hash作为key存入自身时,存入操作改变了Hash的内容,导致它的hash值发生了变化——而Hash查找键是基于当前的hash,不是当初存储时的旧值,所以自然找不到已经存在的键了。

内容的提问来源于stack exchange,提问作者Philip M

火山引擎 最新活动