为什么在Ruby中使用符号作为哈希键?

158 ruby string symbols hashmap

很多时候人们使用符号作为Ruby哈希中的键.

使用字符串有什么好处?

例如:

hash[:name]
Run Code Online (Sandbox Code Playgroud)

hash['name']
Run Code Online (Sandbox Code Playgroud)

Til*_*ilo 216

TL; DR:

使用符号不仅可以节省进行比较的时间,还可以节省内存,因为它们只存储一次.

Ruby符号是不可变的(不能更改),这使得查找更容易

简短(是)答案:

使用符号不仅可以节省进行比较的时间,还可以节省内存,因为它们只存储一次.

Ruby中的符号基本上是"不可变的字符串" ..这意味着它们不能被更改,并且它暗示在整个源代码中多次引用时相同的符号始终存储为同一个实体,例如具有相同的对象ID .

另一方面,字符串是可变的,它们可以随时更改.这意味着Ruby需要将您在源代码中提到的每个字符串存储在它的独立实体中,例如,如果您的源代码中多次提到字符串"name",Ruby需要将这些字符串存储在单独的String对象中,因为它们可能会在以后改变(这是Ruby字符串的本质).

如果你使用一个字符串作为一个哈希键,红宝石需要评估的字符串,它的内容看(和计算上的哈希函数),结果对键的其中已经存储在哈希(散列)值进行比较.

如果你使用符号作为一个哈希键,它是隐含的,它是不可变的,因此Ruby可以基本上只是做对(散列)键的其中已经存储在对象ID对象ID(的哈希函数)的比较哈希.(快多了)

缺点: 每个符号都占用Ruby解释器符号表中的一个插槽,该插槽从未发布.符号永远不会被垃圾收集.因此,当你有大量符号(例如自动生成的符号)时,角落就是一个例子.在这种情况下,您应该评估它如何影响Ruby解释器的大小.

笔记:

如果你进行字符串比较,Ruby可以只通过它们的对象id来比较符号,而不必对它们进行评估.这比比较需要评估的字符串要快得多.

如果您访问哈希,Ruby总是应用哈希函数来计算您使用的任何键的"哈希键".你可以想象像MD5哈希这样的东西.然后Ruby将这些"哈希键"相互比较.

答案很长:

http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol

  • Fyi,符号将在下一版本的Ruby中进行GCd:https://bugs.ruby-lang.org/issues/9634 (4认同)
  • 此外,在Ruby中用作Hash键时,字符串会自动冻结.因此,在这种情况下谈论它们时,字符串是可变的并不完全正确. (2认同)
  • 符号在Ruby 2.2中被垃圾收集 (2认同)
  • 很好的答案!在拖钓方面,您的“简短回答”也足够长。;) (2认同)

Mar*_*une 22

原因是效率,在String上有多个收益:

  1. 符号是不可变的,因此问题是"如果密钥发生变化会发生什么?" 不需要被问到.
  2. 字符串在您的代码中重复,通常会在内存中占用更多空间.
  3. 散列查找必须计算密钥的哈希值以进行比较.这适用O(n)于Strings和Symbols的常量.

此外,Ruby 1.9为带有符号键的哈希引入了简化语法(例如h.merge(foo: 42, bar: 6)),而Ruby 2.0具有仅适用于符号键的关键字参数.

备注:

1)您可能会惊讶地发现Ruby对待String键的方式与任何其他类型不同.确实:

s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash   # must be called whenever a key changes!
h[s]   # => nil, not "bar"
h.keys
h.keys.first.upcase!  # => TypeError: can't modify frozen string
Run Code Online (Sandbox Code Playgroud)

仅对于字符串键,Ruby将使用冻结副本而不是对象本身.

2)对于:bar程序中的所有事件,字母"b","a"和"r"仅存储一次.在Ruby 2.2之前,不断创建Symbols永不重用的新东西是一个坏主意,因为它们将永远保留在全局Symbol查找表中.Ruby 2.2会垃圾收集它们,所以不用担心.

3)实际上,计算Symbol的哈希值并不需要Ruby 1.8.x中的任何时间,因为对象ID是直接使用的:

:bar.object_id == :bar.hash # => true in Ruby 1.8.7
Run Code Online (Sandbox Code Playgroud)

在Ruby 1.9.x中,随着哈希从一个会话变为另一个会话(包括那些会话),这已经发生了变化Symbols:

:bar.hash # => some number that will be different next time Ruby 1.9 is ran
Run Code Online (Sandbox Code Playgroud)


Lar*_*y K 7

Re:使用字符串有什么好处?

  • 样式:它的Ruby方式
  • (非常)略微更快的值查找,因为散列符号相当于散列整数vs散列字符串.

  • 缺点:在程序的符号表中消耗一个永不释放的插槽.

  • 提及该符号永远不会被垃圾收集+1. (4认同)