类 Hash

Hash 将每个唯一的键映射到特定的值。

HashArray 有某些相似之处,但是

Hash 数据语法

较旧的 Hash 数据语法使用“哈希火箭”,即 =>

h = {:foo => 0, :bar => 1, :baz => 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

或者,仅对于 Hash 的键是 Symbol 的情况,可以使用较新的 JSON 风格语法,其中每个裸词都会变成 Symbol

h = {foo: 0, bar: 1, baz: 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

你也可以使用 String 来代替裸词

h = {'foo': 0, 'bar': 1, 'baz': 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

并且可以混合使用这些样式

h = {foo: 0, :bar => 1, 'baz': 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

但是,尝试对非裸词或字符串的键使用 JSON 风格语法是错误的

# Raises SyntaxError (syntax error, unexpected ':', expecting =>):
h = {0: 'zero'}

Hash 的值可以省略,这意味着将从上下文中通过键的名称获取该值

x = 0
y = 100
h = {x:, y:}
h # => {:x=>0, :y=>100}

常用用法

可以使用 Hash 为对象命名

person = {name: 'Matz', language: 'Ruby'}
person # => {:name=>"Matz", :language=>"Ruby"}

可以使用 Hash 为方法参数命名

def some_method(hash)
  p hash
end
some_method({foo: 0, bar: 1, baz: 2}) # => {:foo=>0, :bar=>1, :baz=>2}

注意:当方法调用中的最后一个参数是 Hash 时,可以省略花括号

some_method(foo: 0, bar: 1, baz: 2) # => {:foo=>0, :bar=>1, :baz=>2}

可以使用 Hash 初始化对象

class Dev
  attr_accessor :name, :language
  def initialize(hash)
    self.name = hash[:name]
    self.language = hash[:language]
  end
end
matz = Dev.new(name: 'Matz', language: 'Ruby')
matz # => #<Dev: @name="Matz", @language="Ruby">

创建 Hash

可以使用以下方式显式创建 Hash 对象:

可以使用以下方式将某些对象转换为哈希:

可以通过调用方法 Hash.new 来创建 Hash

创建一个空的 Hash

h = Hash.new
h # => {}
h.class # => Hash

可以通过调用方法 Hash.[] 来创建 Hash

创建一个空的 Hash

h = Hash[]
h # => {}

使用初始条目创建一个 Hash

h = Hash[foo: 0, bar: 1, baz: 2]
h # => {:foo=>0, :bar=>1, :baz=>2}

可以通过使用其字面形式(花括号)来创建 Hash

创建一个空的 Hash

h = {}
h # => {}

使用初始条目创建一个 Hash

h = {foo: 0, bar: 1, baz: 2}
h # => {:foo=>0, :bar=>1, :baz=>2}

Hash 值基础

检索 Hash 值的最简单方法(实例方法 []

h = {foo: 0, bar: 1, baz: 2}
h[:foo] # => 0

创建或更新 Hash 值的最简单方法(实例方法 []=

h = {foo: 0, bar: 1, baz: 2}
h[:bat] = 3 # => 3
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
h[:foo] = 4 # => 4
h # => {:foo=>4, :bar=>1, :baz=>2, :bat=>3}

删除 Hash 条目的最简单方法(实例方法 delete

h = {foo: 0, bar: 1, baz: 2}
h.delete(:bar) # => 1
h # => {:foo=>0, :baz=>2}

条目顺序

Hash 对象按照创建顺序呈现其条目。这在以下情况中可见:

新的 Hash 根据给定的条目具有其初始排序

h = Hash[foo: 0, bar: 1]
h # => {:foo=>0, :bar=>1}

新条目被添加到末尾

h[:baz] = 2
h # => {:foo=>0, :bar=>1, :baz=>2}

更新值不会影响顺序

h[:baz] = 3
h # => {:foo=>0, :bar=>1, :baz=>3}

但是,重新创建已删除的条目可能会影响顺序

h.delete(:foo)
h[:foo] = 5
h # => {:bar=>1, :baz=>3, :foo=>5}

Hash

Hash 键等价性

当两个对象的 hash 值相同且两个对象彼此 eql? 时,它们被视为相同的哈希键。

修改活动的 Hash

在使用 Hash 键时对其进行修改会损坏哈希的索引。

Hash 具有作为数组的键

a0 = [ :foo, :bar ]
a1 = [ :baz, :bat ]
h = {a0 => 0, a1 => 1}
h.include?(a0) # => true
h[a0] # => 0
a0.hash # => 110002110

修改数组元素 a0[0] 会更改其哈希值

a0[0] = :bam
a0.hash # => 1069447059

并损坏 Hash 索引

h.include?(a0) # => false
h[a0] # => nil

可以使用方法 rehash 修复哈希索引

h.rehash # => {[:bam, :bar]=>0, [:baz, :bat]=>1}
h.include?(a0) # => true
h[a0] # => 0

String 键始终是安全的。这是因为作为键传递的未冻结的 String 将被替换为重复且冻结的字符串

s = 'foo'
s.frozen? # => false
h = {s => 0}
first_key = h.keys.first
first_key.frozen? # => true

用户定义的 Hash

要用作 Hash 键,对象必须实现方法 hasheql?。注意:如果 Hash 使用 compare_by_identity,则此要求不适用,因为比较将依赖于键的对象 ID 而不是 hasheql?

Objecthasheq? 定义了基本实现,使每个对象成为一个不同的键。通常,用户定义的类会想要覆盖这些方法以提供有意义的行为,或者例如继承 Struct,它对这些方法有有用的定义。

hash 的典型实现基于对象的数据,而 eql? 通常被别名为覆盖的 == 方法

class Book
  attr_reader :author, :title

  def initialize(author, title)
    @author = author
    @title = title
  end

  def ==(other)
    self.class === other &&
      other.author == @author &&
      other.title == @title
  end

  alias eql? ==

  def hash
    [self.class, @author, @title].hash
  end
end

book1 = Book.new 'matz', 'Ruby in a Nutshell'
book2 = Book.new 'matz', 'Ruby in a Nutshell'

reviews = {}

reviews[book1] = 'Great reference!'
reviews[book2] = 'Nice and compact!'

reviews.length #=> 1

默认值

方法 []values_atdig 需要返回与特定键关联的值。当找不到该键时,该值将由其默认 proc(如果有)确定,否则将是其默认值(初始为“nil”)。

可以使用方法 default 检索默认值

h = Hash.new
h.default # => nil

可以通过将参数传递给方法 Hash.new 或使用方法 default= 来设置默认值

h = Hash.new(-1)
h.default # => -1
h.default = 0
h.default # => 0

当找不到键时,此默认值将返回给 []values_atdig

counts = {foo: 42}
counts.default # => nil (default)
counts[:foo] = 42
counts[:bar] # => nil
counts.default = 0
counts[:bar] # => 0
counts.values_at(:foo, :bar, :baz) # => [42, 0, 0]
counts.dig(:bar) # => 0

请注意,默认值是在不复制的情况下使用的。不建议将默认值设置为可变对象

synonyms = Hash.new([])
synonyms[:hello] # => []
synonyms[:hello] << :hi # => [:hi], but this mutates the default!
synonyms.default # => [:hi]
synonyms[:world] << :universe
synonyms[:world] # => [:hi, :universe], oops
synonyms.keys # => [], oops

要使用可变对象作为默认值,建议使用默认 proc

默认 Proc

Hash 的默认 proc 被设置(即不是 nil)时,方法 [] 返回的默认值仅由默认 proc 确定。

可以使用方法 default_proc 检索默认 proc

h = Hash.new
h.default_proc # => nil

可以通过调用带有块的 Hash.new 或调用方法 default_proc= 来设置默认 proc

h = Hash.new { |hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
h.default_proc = proc { |hash, key| "Default value for #{key.inspect}" }
h.default_proc.class # => Proc

当设置了默认 proc(即不是 nil)并且使用不存在的键调用方法 [] 时,[] 将使用 Hash 对象本身和缺失的键调用默认 proc,然后返回 proc 的返回值

h = Hash.new { |hash, key| "Default value for #{key}" }
h[:nosuch] # => "Default value for nosuch"

请注意,在上面的示例中,没有为键 :nosuch 创建条目

h.include?(:nosuch) # => false

但是,proc 本身可以添加新条目

synonyms = Hash.new { |hash, key| hash[key] = [] }
synonyms.include?(:hello) # => false
synonyms[:hello] << :hi # => [:hi]
synonyms[:world] << :universe # => [:universe]
synonyms.keys # => [:hello, :world]

请注意,设置默认 proc 将清除默认值,反之亦然。

请注意,修改哈希的默认 proc 不是线程安全的,因为多个线程可以同时为同一个键调用默认 proc。

此处内容

首先,其他地方的内容。类 Hash

此处,类 Hash 提供了对以下操作有用的方法:

Hash 还包括来自模块 Enumerable 的方法。

用于创建 Hash 的方法

用于设置 Hash 状态的方法

用于查询的方法

用于比较的方法

用于获取的方法

用于赋值的方法

用于删除的方法

这些方法从 self 中删除条目

这些方法返回 self 的副本,并删除了一些条目

用于迭代的方法

用于转换的方法

用于转换键和值的方法

其他方法