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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8189416/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:31:03  来源:igfitidea点击:

Why use symbols as hash keys in Ruby?

rubystringhashmapsymbols

提问by Tilo

A lot of times people use symbols as keys in a Ruby hash.

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

What's the advantage over using a string?

与使用字符串相比有什么优势?

E.g.:

例如:

hash[:name]

vs.

对比

hash['name']

回答by Tilo

TL;DR:

特尔;博士:

Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.

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

Ruby Symbols are immutable (can't be changed), which makes looking something up much easier

Ruby Symbols 是不可变的(不能改变),这使得查找更容易

Short(ish) answer:

简短(ish)答案:

Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.

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

Symbols in Ruby arebasically "immutable strings".. that means that they can not be changed, and it implies that the same symbol when referenced many times throughout your source code, is always stored as the same entity, e.g. has the same object id.

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

Strings on the other hand are mutable, they can be changed anytime. This implies that Ruby needs to store each string you mention throughout your source code in it's separate entity, e.g. if you have a string "name" multiple times mentioned in your source code, Ruby needs to store these all in separate String objects, because they might change later on (that's the nature of a Ruby string).

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

If you use a string as a Hash key, Ruby needs to evaluate the string and look at it's contents (and compute a hash function on that) and compare the result against the (hashed) values of the keys which are already stored in the Hash.

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

If you use a symbol as a Hash key, it's implicit that it's immutable, so Ruby can basically just do a comparison of the (hash function of the) object-id against the (hashed) object-ids of keys which are already stored in the Hash. (much faster)

如果你使用一个符号作为哈希键,它隐含着它是不可变的,所以 Ruby 基本上可以将对象 ID 的(哈希函数)与已经存储在其中的键的(哈希)对象 ID 进行比较哈希。(快多了)

Downside:Each symbol consumes a slot in the Ruby interpreter's symbol-table, which is never released. Symbols are never garbage-collected. So a corner-case is when you have a large number of symbols (e.g. auto-generated ones). In that case you should evaluate how this affects the size of your Ruby interpreter.

缺点:每个符号都占用 Ruby 解释器符号表中的一个插槽,该插槽永远不会被释放。符号永远不会被垃圾收集。因此,当您有大量符号(例如自动生成的符号)时,会出现极端情况。在这种情况下,您应该评估这如何影响 Ruby 解释器的大小。

Notes:

笔记:

If you do string comparisons, Ruby can compare symbols just by their object ids, without having to evaluate them. That's much faster than comparing strings, which need to be evaluated.

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

If you access a hash, Ruby always applies a hash-function to compute a "hash-key" from whatever key you use. You can imagine something like an MD5-hash. And then Ruby compares those "hashed keys" against each other.

如果您访问散列,Ruby 总是应用散列函数从您使用的任何键计算“散列键”。你可以想象像 MD5 哈希这样的东西。然后 Ruby 将这些“散列键”相互比较。

Long answer:

长答案:

https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

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/

http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/

回答by Marc-André Lafortune

The reason is efficiency, with multiple gains over a String:

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

  1. Symbols are immutable, so the question "what happens if the key changes?" doesn't need to be asked.
  2. Strings are duplicated in your code and will typically take more space in memory.
  3. Hash lookups must compute the hash of the keys to compare them. This is O(n)for Strings and constant for Symbols.
  1. 符号是不可变的,所以问题是“如果密钥更改会发生什么?” 不需要问。
  2. 字符串在您的代码中是重复的,通常会占用更多的内存空间。
  3. 哈希查找必须计算键的哈希值来比较它们。这是O(n)用于字符串和常量用于符号。

Moreover, Ruby 1.9 introduced a simplified syntax just for hash with symbols keys (e.g. h.merge(foo: 42, bar: 6)), and Ruby 2.0 has keyword argumentsthat work only for symbol keys.

此外,Ruby 1.9 引入了一种仅用于带有符号键(例如h.merge(foo: 42, bar: 6))的哈希的简化语法,而 Ruby 2.0 具有仅适用于符号键的关键字参数

Notes:

注意事项

1) You might be surprised to learn that Ruby treats Stringkeys differently than any other type. Indeed:

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

For string keys only, Ruby will use a frozen copy instead of the object itself.

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

2) The letters "b", "a", and "r" are stored only once for all occurrences of :barin a program. Before Ruby 2.2, it was a bad idea to constantly create new Symbolsthat were never reused, as they would remain in the global Symbol lookup table forever. Ruby 2.2 will garbage collect them, so no worries.

2) 字母“b”、“a”和“r”:bar在程序中的所有出现中只存储一次。在 Ruby 2.2 之前,不断创建Symbols从未重用的新元素是一个坏主意,因为它们将永远保留在全局 Symbol 查找表中。Ruby 2.2 会垃圾收集它们,所以不用担心。

3) Actually, computing the hash for a Symbol didn't take any time in Ruby 1.8.x, as the object ID was used directly:

3) 实际上,在 Ruby 1.8.x 中计算 Symbol 的哈希值并不需要任何时间,因为直接使用了对象 ID:

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

In Ruby 1.9.x, this has changed as hashes change from one session to another (including those of Symbols):

在 Ruby 1.9.x 中,随着哈希值从一个会话更改为另一个会话(包括 的那些Symbols),这已经发生了变化:

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

回答by Larry K

Re: what's the advantage over using a string?

Re: 与使用字符串相比有什么优势?

  • Styling: its the Ruby-way
  • (Very) slightly faster value look ups since hashing a symbol is equivalent to hashing an integer vs hashing a string.

  • Disadvantage: consumes a slot in the program's symbol table that is never released.

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

  • 缺点:消耗程序符号表中一个永远不会释放的槽。

回答by jlecour

I'd be very interested in a follow-up regarding frozen strings introduced in Ruby 2.x.

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

When you deal with numerous strings coming from a text input (I'm thinking of HTTP params or payload, through Rack, for example), it's way easier to use strings everywhere.

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

When you deal with dozens of them but they never change (if they're your business "vocabulary"), I like to think that freezing them can make a difference. I haven't done any benchmark yet, but I guess it would be close the symbols performance.

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