Ruby 哈希(Hash)
在 Ruby 中,哈希(Hash) 是一种键值对(key-value pair)的数据结构,用于存储和快速查找数据。哈希的键和值可以是任意 Ruby 对象,键必须唯一。Ruby 的哈希功能强大,提供了丰富的内置方法来操作键值对。以下是对 Ruby 哈希的中文讲解,涵盖创建、操作、常见方法等关键内容,力求简洁清晰。
1. 创建哈希
Ruby 提供多种方式创建哈希:
- 字面量:使用大括号
{}
,键值对用=>
或:
(符号键)分隔。
hash = { "name" => "Alice", "age" => 25 }
# 使用符号键(更常见)
hash = { name: "Alice", age: 25 }
puts hash # 输出:{:name=>"Alice", :age=>25}
- Hash.new:通过构造方法创建。
hash = Hash.new # 空哈希
hash = Hash.new("default") # 默认值
puts hash[:unknown] # 输出:default
- 数组转换:用
to_h
或Hash[]
。
arr = [[:name, "Alice"], [:age, 25]]
hash = arr.to_h
puts hash # 输出:{:name=>"Alice", :age=>25}
说明:
- 符号(
Symbol
)常作为键,因其内存效率高且不可变。 - 键值对用逗号分隔,
:
是符号键的简写,等价于=>
。
2. 访问和修改哈希
- 访问值:通过键访问,使用
[]
。
hash = { name: "Alice", age: 25 }
puts hash[:name] # 输出:Alice
puts hash[:age] # 输出:25
- 修改值:通过键赋值。
hash[:age] = 26
puts hash # 输出:{:name=>"Alice", :age=>26}
- 默认值:未找到键时返回默认值(默认
nil
)。
hash = Hash.new("unknown")
puts hash[:missing] # 输出:unknown
- fetch 方法:更安全地访问,键不存在时可抛出错误或指定默认值。
hash = { name: "Alice" }
puts hash.fetch(:name) # 输出:Alice
puts hash.fetch(:age, 18) # 输出:18(默认值)
# puts hash.fetch(:age) # 抛出 KeyError
3. 常见哈希方法
Ruby 提供丰富的哈希方法,简化操作:
添加/删除键值对
- 添加:直接赋值或用
store
。
hash = {}
hash[:name] = "Bob"
hash.store(:age, 30)
puts hash # 输出:{:name=>"Bob", :age=>30}
- 删除:
delete
删除指定键并返回其值。
hash = { name: "Alice", age: 25 }
puts hash.delete(:age) # 输出:25
puts hash # 输出:{:name=>"Alice"}
- clear:清空哈希。
hash.clear
puts hash # 输出:{}
查询与检查
key?
或has_key?
:检查键是否存在。
hash = { name: "Alice" }
puts hash.key?(:name) # 输出:true
puts hash.key?(:age) # 输出:false
value?
或has_value?
:检查值是否存在。
puts hash.value?("Alice") # 输出:true
empty?
:检查哈希是否为空。
puts {}.empty? # 输出:true
length
或size
:返回键值对数量。
hash = { name: "Alice", age: 25 }
puts hash.length # 输出:2
迭代与转换
each
:遍历键值对。
hash = { name: "Alice", age: 25 }
hash.each { |key, value| puts "#{key}: #{value}" }
# 输出:
# name: Alice
# age: 25
each_key
和each_value
:分别遍历键或值。
hash.each_key { |key| puts key } # 输出:name, age
hash.each_value { |value| puts value } # 输出:Alice, 25
map
:转换键值对,返回数组。
hash = { a: 1, b: 2 }
result = hash.map { |k, v| "#{k}=#{v}" }
puts result # 输出:["a=1", "b=2"]
select
:筛选符合条件的键值对。
hash = { a: 1, b: 2, c: 3 }
filtered = hash.select { |k, v| v > 1 }
puts filtered # 输出:{:b=>2, :c=>3}
reject
:筛选不符合条件的键值对。
filtered = hash.reject { |k, v| v > 1 }
puts filtered # 输出:{:a=>1}
合并与更新
merge
:合并两个哈希,返回新哈希。
hash1 = { a: 1, b: 2 }
hash2 = { b: 3, c: 4 }
puts hash1.merge(hash2) # 输出:{:a=>1, :b=>3, :c=>4}
merge!
或update
:合并并修改原哈希。
hash1.merge!(hash2)
puts hash1 # 输出:{:a=>1, :b=>3, :c=>4}
4. 键的类型
哈希的键可以是任意对象,但符号(Symbol
)最常用,因其高效且不可变。
hash = { 1 => "number", "key" => "string", :sym => "symbol" }
puts hash[1] # 输出:number
puts hash[:sym] # 输出:symbol
5. 哈希与 Enumerable 模块
哈希包含 Enumerable
模块,支持迭代方法(如 map
, select
, find
)。
hash = { a: 1, b: 2, c: 3 }
puts hash.find { |k, v| v > 2 } # 输出:[:c, 3]
puts hash.sum { |k, v| v } # 输出:6(值的和)
6. 冻结哈希
哈希默认可变,可用 freeze
使其不可修改。
hash = { name: "Alice" }.freeze
# hash[:age] = 25 # 错误:Cannot modify frozen Hash
7. 注意事项
- 键唯一性:同一键重复赋值会覆盖旧值。
hash = { a: 1, a: 2 }
puts hash # 输出:{:a=>2}
- 性能:哈希查找效率高(接近 O(1)),适合键值映射。
- 符号 vs 字符串:符号键更高效,适合固定键;字符串键适合动态键。
- 默认值陷阱:
Hash.new([])
默认值是同一对象,需谨慎。
hash = Hash.new([])
hash[:a] << 1
puts hash[:b] # 输出:[1](共享默认数组)
解决:用块动态生成默认值。
hash = Hash.new { |h, k| h[k] = [] }
hash[:a] << 1
puts hash[:b] # 输出:[](独立数组)
8. 示例:综合应用
module HashUtils
def print_pairs
each { |k, v| puts "#{k} => #{v}" }
end
end
class Hash
include HashUtils
end
hash = { name: "Alice", age: 25, city: "New York" }
hash.print_pairs
# 输出:
# name => Alice
# age => 25
# city => New York
puts hash.select { |k, v| v.is_a?(String) } # 输出:{:name=>"Alice", :city=>"New York"}
总结
Ruby 的哈希是键值存储的强大工具,支持灵活的键类型(符号最常见)和丰富的操作方法。结合 Enumerable
模块,哈希可以高效处理数据迭代和筛选。注意键唯一性、默认值行为和可变性,有助于编写高效且安全的代码。
如果你有具体问题或需要更详细的示例,请告诉我!