Hash主要使用Key中的两个函数:hash和eql? hash返回的是对象的hash值,eql?是用来对比两个对象是不是相等。
回顾下Hash这种数据结构,其首先需要一个hash函数返回一个整数,ruby中为Fixnum,然后根据Fixnum去使用eql?判断两个object是不是相同。
所以,如果只是改变了hash函数,就仅仅会让具有相同实例的objecthash到同一个Fixnum,但是eql?不相等,也没有办法当成同一个key。同样,如果只修改eql?,相同字段的object可能都没有办法hash到同一个Fixnum,那么同样也没有办法当作同一个key。
为了说明这两个函数的用处,我将分四种情况讨论。
情况1)定义一个Kiwi,不改变hash和eql?
class Kiwi attr_reader :id, :name def initialize(id, name) @id = id @name = name end end Kiwi.new(1, "kiwi").eql?(Kiwi.new(1, "kiwi")) #=> false hash = Hash.new #=> {} hash[Kiwi.new(1, "kiwi")] = 1 #=> {#<Kiwi:0x00000001ec8230 @id=1, @name="kiwi">=>1} hash[Kiwi.new(1, "kiwi")] #=> nil
从情况1中,我们可以看出在没有重写hash和eql?的情况下,虽然Kiwi的两个实例字段相同,但是不能相等,同时,hash具有相同字段的Kiwi实例,也不能得到结果。
情况2)仅修改hash,不修改eql?
class Kiwi attr_reader :id, :name def initialize(id, name) @id = id @name = name end def hash @id.hash end end Kiwi.new(1, "kiwi").eql?(Kiwi.new(1, "kiwi")) #=> false hash = Hash.new #=> {} hash[Kiwi.new(1, "kiwi")] = 1 #=> {#<Kiwi:0x00000000c38de0 @id=1, @name="kiwi">=>1} hash[Kiwi.new(1, "kiwi")] #=> nil
结果与情况1中相同
情况3)仅修改eql?
class Kiwi attr_reader :id, :name def initialize(id, name) @id = id @name = name end def eql?(other) self.class.equal?(other.class) && @id == other.id && @name == other.name end end Kiwi.new(1, "kiwi").eql?(Kiwi.new(1, "kiwi")) #=> true hash = Hash.new #=> {} hash[Kiwi.new(1, "kiwi")] = 1 #=>{#<Kiwi:0x00000000f5ba90 @id=1, @name="kiwi">=>1} hash[Kiwi.new(1, "kiwi")] #=> nil
可以看出当Kiwi两个实例变量相同的相同的时候,Kiwi即相等
情况4)同时修改hash和eql?
class Kiwi attr_reader :id, :name def initialize(id, name) @id = id @name = name end def eql?(other) self.class.equal?(other.class) && @id == other.id && @name == other.name end def hash @id.hash end end Kiwi.new(1, "kiwi").eql?(Kiwi.new(1, "kiwi")) #=> true hash = Hash.new #=> {} hash[Kiwi.new(1, "kiwi")] = 1 #=> {#<Kiwi:0x00000000b928f0 @id=1, @name="kiwi">=>1} hash[Kiwi.new(1, "kiwi")] #=> 1
这下就只要kiwi的实例变量相同,都被当成同一个key了