Answers:
TL; DR:
使用符号不仅可以节省进行比较的时间,还可以节省内存,因为它们仅存储一次。
Ruby符号是不可变的(无法更改),这使查找内容变得更加容易
简短答案:
使用符号不仅可以节省进行比较的时间,还可以节省内存,因为它们仅存储一次。
Ruby中的符号基本上是“不可变的字符串” ..这意味着它们无法更改,并且意味着在源代码中多次引用相同的符号时,该符号始终存储为相同的实体,例如具有相同的对象ID 。
另一方面,字符串是可变的,可以随时更改。这意味着Ruby需要将您在整个源代码中提到的每个字符串存储在单独的实体中,例如,如果您在源代码中多次提到字符串“ name”,则Ruby需要将所有这些都存储在单独的String对象中,因为它们稍后可能会更改(这是Ruby字符串的本质)。
如果您使用字符串作为哈希键,则Ruby需要评估该字符串并查看其内容(并在其上计算哈希函数),然后将结果与哈希中已存储的键的(哈希)值进行比较。
如果使用符号作为哈希键,则隐含它是不可变的,因此Ruby基本上可以将对象ID的(哈希函数)与已经存储在其中的键的(哈希)对象ID进行比较。哈希。(快多了)
缺点: 每个符号都占用Ruby解释器的符号表中的一个插槽,该插槽从不释放。符号永远不会被垃圾收集。因此,当您有大量符号(例如,自动生成的符号)时,便是一个极端情况。在这种情况下,您应该评估这如何影响Ruby解释器的大小。
笔记:
如果进行字符串比较,Ruby可以仅通过符号的对象ID来比较符号,而不必对其进行评估。这比比较需要评估的字符串要快得多。
如果您访问哈希,Ruby始终会应用哈希函数来根据您使用的任何密钥来计算“哈希密钥”。您可以想象像MD5哈希这样的东西。然后Ruby将这些“哈希键”相互比较。
长答案:
原因是效率,在String上有多个收益:
O(n)
用于字符串,常量用于符号。而且,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
仅对于字符串键,Ruby将使用冻结副本而不是对象本身。
2)对于:bar
程序中所有出现的字母“ b”,“ a”和“ r”仅存储一次。在Ruby 2.2之前,不断创建Symbols
从未重用的新代码是一个坏主意,因为它们将永远保留在全局Symbol查找表中。Ruby 2.2会垃圾回收它们,所以不用担心。
3)实际上,在Ruby 1.8.x中无需花费任何时间计算Symbol的哈希值,因为直接使用了对象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
回复:使用字符串有什么好处?
(非常)略微加快了值查找的速度,因为哈希符号等效于哈希整数与哈希字符串。
缺点:占用了程序符号表中从未释放的插槽。