【发布时间】:2012-01-01 15:07:09
【问题描述】:
很多时候人们在 Ruby 哈希中使用符号作为键。
与使用字符串相比有什么优势?
例如:
hash[:name]
对比
hash['name']
【问题讨论】:
标签: ruby string hashmap symbols
很多时候人们在 Ruby 哈希中使用符号作为键。
与使用字符串相比有什么优势?
例如:
hash[:name]
对比
hash['name']
【问题讨论】:
标签: ruby string hashmap symbols
TL;DR:
使用符号不仅可以节省比较时间,还可以节省内存,因为它们只存储一次。
Ruby 符号是不可变的(无法更改),这使得查找内容变得更加容易
简短的回答:
使用符号不仅可以节省比较时间,还可以节省内存,因为它们只存储一次。
Ruby 中的符号基本上是 “不可变字符串” .. 这意味着它们不能被更改,这意味着在您的整个过程中多次引用相同的符号时源代码,总是存储为同一个实体,例如具有相同的对象 ID。
另一方面,字符串是可变的,它们可以随时更改。这意味着 Ruby 需要将您在整个源代码中提到的每个字符串存储在它的单独实体中,例如如果您的源代码中多次提到了一个字符串“名称”,Ruby 需要将这些全部存储在单独的 String 对象中,因为它们以后可能会发生变化(这是 Ruby 字符串的本质)。
如果您使用字符串作为哈希键,Ruby 需要评估字符串并查看其内容(并在其上计算哈希函数)并将结果与已存储的键的(哈希)值进行比较在哈希中。
如果您使用符号作为哈希键,则暗示它是不可变的,因此 Ruby 基本上可以将对象 ID 的(哈希函数)与键的(哈希)对象 ID 进行比较,这些键是已经存储在哈希中。 (快得多)
缺点: 每个符号都使用 Ruby 解释器符号表中的一个槽,该槽永远不会被释放。 符号永远不会被垃圾收集。 因此,极端情况是当您拥有大量符号(例如自动生成的符号)时。在这种情况下,您应该评估这如何影响您的 Ruby 解释器的大小。
注意事项:
如果您进行字符串比较,Ruby 可以仅通过比较它们的对象 id 来比较符号,而无需评估它们。这比比较需要评估的字符串快得多。
如果您访问散列,Ruby 总是应用散列函数从您使用的任何键计算“散列键”。您可以想象像 MD5 哈希这样的东西。然后 Ruby 将这些“散列键”相互比较。
每次在代码中使用字符串时,都会创建一个新实例 - 创建字符串比引用符号要慢。
从 Ruby 2.1 开始,当您使用冻结字符串时,Ruby 将使用相同的字符串对象。这避免了创建相同字符串的新副本,并将它们存储在垃圾回收的空间中。
长答案:
【讨论】:
我对 Ruby 2.x 中引入的冻结字符串的后续工作非常感兴趣。
当您处理来自文本输入的大量字符串时(例如,我正在考虑通过 Rack 的 HTTP 参数或负载),在任何地方使用字符串会更容易。
当您处理数十个但它们从未改变时(如果它们是您的业务“词汇”),我喜欢认为冻结它们会有所作为。我还没有做任何基准测试,但我想这将接近符号性能。
【讨论】:
原因是效率,比字符串有多重收益:
O(n) 对于字符串和符号常量。此外,Ruby 1.9 引入了一种简化的语法,仅用于带有符号键的哈希(例如 h.merge(foo: 42, bar: 6)),而 Ruby 2.0 具有仅适用于符号键的 keyword arguments。
注意事项:
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
仅对于字符串键,Ruby 将使用冻结副本而不是对象本身。
2) 对于程序中所有出现的:bar,字母“b”、“a”和“r”只存储一次。在 Ruby 2.2 之前,不断创建从未重复使用的新 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
【讨论】:
Re: 比使用字符串有什么好处?
(非常)更快的值查找,因为散列符号相当于散列整数与散列字符串。
缺点:占用程序符号表中的一个永远不会释放的槽。
【讨论】: