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

时间:2011-11-18 21:30:53

标签: ruby string hashmap symbols

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

使用字符串有什么好处?

E.g:

hash[:name]

VS

hash['name']

4 个答案:

答案 0 :(得分:209)

<强> TL; DR:

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

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

简短(是)答案:

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

Ruby中的符号基本上“不可变的字符串” ..这意味着它们无法更改,并且它意味着在您的整个过程中多次引用相同的符号源代码始终存储为同一实体,例如具有相同的对象ID。

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

如果使用字符串作为哈希键,Ruby需要评估字符串并查看其内容(并计算其上的哈希函数)并将结果与​​已存储的键的(哈希)值进行比较在哈希。

如果你使用一个符号作为Hash键,它暗示它是不可变的,所以Ruby基本上只是将object-id的(哈希函数)与键的(哈希)对象-id进行比较。已存储在Hash中。 (快得多)

<强>缺点: 每个符号都占用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

答案 1 :(得分:21)

原因是效率,在字符串上有多个收益:

  1. 符号是不可变的,因此问题是“如果密钥发生变化会发生什么?”不需要被问到。
  2. 字符串在您的代码中重复,通常会在内存中占用更多空间。
  3. 哈希查找必须计算密钥的哈希值以进行比较。字符串为O(n),符号为常量。
  4. 此外,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的所有Symbols,字母“b”,“a”和“r”仅存储一次。在Ruby 2.2之前,不断创建从未重用的新:bar.object_id == :bar.hash # => true in Ruby 1.8.7 是一个坏主意,因为它们将永远保留在全局Symbol查找表中。 Ruby 2.2会垃圾收集它们,所以不用担心。

    3)实际上,计算一个Symbol的哈希在Ruby 1.8.x中没有花费任何时间,因为对象ID是直接使用的:

    Symbols

    在Ruby 1.9.x中,随着哈希从一个会话变为另一个会话(包括:bar.hash # => some number that will be different next time Ruby 1.9 is ran 的会话),这已经发生了变化:

    {{1}}

答案 2 :(得分:7)

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

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

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

答案 3 :(得分:0)

我对Ruby 2.x中引入的冻结字符串的跟进非常感兴趣。

当您处理来自文本输入的大量字符串时(例如,我正在考虑HTTP参数或有效负载,通过Rack),在任何地方使用字符串都会更容易。

当你处理其中的几十个但它们从未改变时(如果它们是你的业务“词汇”),我喜欢认为冻结它们会产生影响。我还没有做过任何基准测试,但我想它会接近符号性能。