很多时候,人们在 Ruby 散列中使用符号作为键。
使用字符串有什么好处?
例如:
hash[:name]
对。
hash['name']
译者:
使用符号不仅可以节省比较时间,还可以节省内存,因为它们只存储一次。
Ruby 符号是不可变的(不能更改) ,这使得查找更加容易
简短的回答:
Ruby 中的符号基本上是 “不可变的字符串”。.这意味着它们不能被更改,这意味着当在源代码中多次引用相同的符号时,它们总是存储为相同的实体,例如,具有相同的对象 ID。
另一方面,字符串是可变的 ,它们可以随时更改。这意味着 Ruby 需要将你在源代码中提到的每个字符串存储在它独立的实体中,例如,如果你在源代码中多次提到一个字符串“ name”,Ruby 需要将这些都存储在单独的 String 对象中,因为它们可能会在以后发生变化(这是 Ruby 字符串的本质)。
如果你使用一个字符串作为哈希键,Ruby 需要计算这个字符串并查看它的内容(并计算一个哈希函数) ,然后将结果与已经存储在哈希中的键的(哈希)值进行比较。
如果你使用一个符号作为一个 Hash 键,那么它是不可变的,所以 Ruby 基本上只需要比较一下(Hash 函数的) object-id 和(Hash) object-ids 中已经存储在 Hash 中的键。(更快)
缺点: 每个符号消耗 Ruby 解释器符号表中的一个槽,这个槽永远不会被释放。 符号永远不会被垃圾收集。 所以当你有大量的符号(例如自动生成的符号)时,就是一个极端的情况。在这种情况下,您应该评估这对 Ruby 解释器大小的影响。
备注:
如果进行字符串比较,Ruby 可以通过比较符号的对象 id 来进行比较,而不必计算它们。这比比较需要计算的字符串要快得多。
如果您访问一个 hash,Ruby 总是应用一个 hash 函数从您使用的任何键计算一个“ hash-key”。您可以想象类似 MD5-hash 的东西。然后 Ruby 将这些“散列键”进行对比。
每次在代码中使用字符串时,都会创建一个新实例——字符串创建比引用符号慢。
从 Ruby 2.1开始,当您使用冻结字符串时,Ruby 将使用相同的字符串对象。这样就避免了创建同一字符串的新副本,并且它们被存储在垃圾回收的空间中。
冗长的回答:
Https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings
Http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/
Https://www.rubyguides.com/2016/01/ruby-mutability/
回应: 使用字符串有什么好处?
(非常)稍微快一点的值查找,因为散列一个符号相当于散列一个整数与散列一个字符串。
缺点: 消耗程序符号表中永远不会释放的槽。
原因在于效率,比 String 有多重收益:
O(n)
此外,Ruby 1.9引入了一种简化的语法,只适用于带有符号键的 hash (例如 h.merge(foo: 42, bar: 6)) ,而 Ruby 2.0具有仅适用于符号键的 关键字参数。
h.merge(foo: 42, bar: 6)
备注 :
1)你可能会惊讶地发现 Ruby 对待 String键的方式不同于其他任何类型:
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)字母“ b”、“ a”和“ r”对于程序中所有 :bar的出现只存储一次。在 Ruby 2.2之前,不断创建从未被重用的新 Symbols是一个糟糕的主意,因为它们将永远保留在全局符号查找表中。Ruby 2.2会垃圾收集它们,所以不用担心。
:bar
Symbols
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
我对 Ruby 2.x 中引入的关于冻结字符串的后续内容非常感兴趣。
当您处理来自文本输入的大量字符串时(例如,我想到的是 HTTP 参数或有效负载,通过 Rack) ,在任何地方使用字符串都要容易得多。
当你和几十个人打交道,但他们从来没有改变(如果他们是你的业务“词汇”) ,我喜欢认为冻结他们可以有所不同。我还没有做任何基准测试,但我想它会接近符号的性能。