Sunday, October 19, 2008

(15)Ruby 中的字典类

一个字典对象就是包含一系列键/值对映射的数据结构。它和数组类似,不过,数组只能通过整数索引来操作数组内的元素,而字典对象中的“索引”可以是任何对象,下面是一个例子:
numbers = Hash.new     # 创建一个新的、空字典对象
numbers["one"] = 1 # 将字符串"one"映射到Fixnum 1上
numbers["two"] = 2 # 这里类似于数组的下标赋值方式,但是这里的下标可以是任何对象
numbers["three"] = 3

sum = numbers["one"] + numbers["two"] # 通过下标的方式取得字典对象中的元素

一个字典对象中的元素包含在一对{}中,其中的元素是由逗号分隔的键/值对,键和值之间通过一个=>分隔。我们上面例子中的字典对象还可以写成这样:
numbers={"one"=>1,"two"=>2,"three"=>3}

通常我们使用符号对象作为字典元素的
numbers={:one=>1,:two=>2,:three=>3}

Ruby1.8还支持另一种全部用逗号分隔的写法,但是Ruby1.9中将不再支持这种方式:
numbers = { :one, 1, :two, 2, :three, 3 } # 可以实现相同的功能但是可读性不好

Ruby 1.9中提供了另一种方便而精炼的定义方式,直接用符号对象,但是冒号放到了符号对象的右边,而且取消了=>的使用:
numbers = { one: 1, two: 2, three: 3 }

注意冒号的两边不能有空格。
Ruby 中的字典对象其实是实现了哈希表,这意味着字典元素中的”键“必须有一个hash方法,这个方法返回”键“的Fixnum型的哈希代码。那么,在比较字典元素的键是否相等时,实际上比较的是他们的哈希代码。一个字典类通过eql?方法来比较元素的键是否相等,所以如果你用到了自定义对象作为”键“,那么你的自定义对象必须重载hash方法,如果你不重载这个方法那么即使你的对象内容是一样的,那么在调用eql?方法进行比较的时候,结果还是false,因为缺省情况下,hash方法返回的是对象的ID,而每一个对象的ID都是唯一。
注意,使用可变对象作为”键“可能会带来麻烦,因为如果你更改了可变对象的值,你实际上也更改了他的哈希代码。
字符串对象是一个可变对象,但是它经常作为字典对象的键,所以Ruby对它进行了特殊的处理,如果一个字符串对象作为键的时候,Ruby会创建一个”私有的对象拷贝“。但是这只是一个特例,在你使用其他可变对象作为键的时候,你应该考虑也”创建一个私有的对象拷贝“或者调用freeze方法,防止这个对象被更改。如果你不得不修改这个可变的”键“,那么记住在你修改这个键之后调用Hash类的rehash方法。否则字典对象中的元素会出现错误。

No comments: