为什么在Ruby中使用符号作为散列键?

很多时候,人们在Ruby哈希中使用符号作为键。

与使用string相比,有什么优势?

例如:

hash[:name] 

 hash['name'] 

简短的回答:

使用符号不仅节省了比较时间,而且节省了内存,因为它们只存储一次。

在Ruby中的符号基本上是“不可变的string” ..这意味着它们不能被改变,这意味着在你的源代码中多次引用相同的符号总是存储为相同的实体,例如具有相同的对象ID 。

另一方面string是可变的 ,可以随时更改。 这意味着Ruby需要将您在源代码中提到的每个string存储在其独立的实体中,例如,如果在源代码中多次提到一个string“name”,则Ruby需要将这些string存储在单独的String对象中,因为它们可能会稍后改变(这是一个Rubystring的性质)。

如果你使用一个string作为哈希键,Ruby需要计算string并查看它的内容(并计算哈希函数),并将结果与​​已存储在哈希表中的哈希值(哈希值)进行比较。

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

下行:每个符号在Ruby解释器的符号表中占用一个插槽,从不释放。 符号从来没有垃圾收集。 所以,当你有大量的符号(例如自动生成的符号)的时候,就会出现这种情况。 在这种情况下,您应该评估这会如何影响Ruby解释器的大小。

笔记:

如果您进行string比较,Ruby可以仅通过对象ID来比较符号,而不必评估它们。 这比比较需要评估的string要快得多。

如果你访问一个散列,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

原因是效率,与string的多个收益:

  1. 符号是不可改变的,所以问题“如果密钥更改会发生什么?” 不需要问。
  2. string在你的代码中被复制,并且通常会在内存中占用更多的空间。
  3. 哈希查找必须计算密钥的哈希来比较它们。 这是string的O(n)和符号的常量。

而且,Ruby 1.9引入了一个简单的语法,只是用符号键(例如h.merge(foo: 42, bar: 6) )进行散列,而Ruby 2.0的关键字参数只适用于符号键。

备注

1)您可能会惊讶地发现,Ruby对待String键的方式不同于其他types。 确实:

 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 

仅对于string键,Ruby将使用冻结副本而不是对象本身。

2)字母“b”,“a”和“r”在程序中只存储一次:bar 。 在Ruby 2.2之前,不断创build从未被重用的新Symbols是一个糟糕的主意,因为它们将永远留在全局符号查找表中。 Ruby 2.2将垃圾收集起来,所以不用担心。

3)实际上,在Ruby 1.8.x中计算符号的散列并不需要任何时间,因为对象ID是直接使用的:

 :bar.object_id == :bar.hash # => true in Ruby 1.8.7 

在Ruby 1.9.x中,随着哈希从一个会话改变到另一个会话(包括Symbols ),这个改变了:

 :bar.hash # => some number that will be different next time Ruby 1.9 is ran 

回复:使用string有什么优势?

  • 造型:它的Ruby方式
  • (非常)稍微快一点的值查找,因为散列符号相当于散列整数与散列string。

  • 缺点:在程序的符号表中占用一个从不释放的插槽。

我对Ruby 2.x中引入的冻结string的后续工作非常感兴趣。

当处理来自文本input的大量string时(例如,通过Rack,我正在考虑使用HTTP参数或有效负载),在各处使用string更容易。

当你和几十个人打交道,但他们从不改变(如果他们是你的业务“词汇”),我喜欢认为冻结他们可以有所作为。 我还没有做任何基准,但我想这将是closures符号的performance。