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_hHash[]
  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
  • lengthsize:返回键值对数量。
  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_keyeach_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 模块,哈希可以高效处理数据迭代和筛选。注意键唯一性、默认值行为和可变性,有助于编写高效且安全的代码。

如果你有具体问题或需要更详细的示例,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注