代码改变世界

ruby中Hash的自定义key

2012-11-22 11:35  java20130722  阅读(392)  评论(0编辑  收藏  举报

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了