Por que usar símbolos como chaves de hash no Ruby?


161

Muitas vezes as pessoas usam símbolos como chaves em um hash Ruby.

Qual é a vantagem de usar uma string?

Por exemplo:

hash[:name]

vs.

hash['name']

Respostas:


226

TL; DR:

O uso de símbolos não apenas economiza tempo ao fazer comparações, mas também economiza memória, porque eles são armazenados apenas uma vez.

Os símbolos Ruby são imutáveis ​​(não podem ser alterados), o que facilita muito a busca de algo

Resposta curta (ish):

O uso de símbolos não apenas economiza tempo ao fazer comparações, mas também economiza memória, porque eles são armazenados apenas uma vez.

Os símbolos no Ruby são basicamente "strings imutáveis" . Isso significa que eles não podem ser alterados e implica que o mesmo símbolo, quando referenciado muitas vezes em todo o código-fonte, é sempre armazenado como a mesma entidade, por exemplo, tem o mesmo ID de objeto .

As strings, por outro lado, são mutáveis , podem ser alteradas a qualquer momento. Isso implica que o Ruby precise armazenar cada string mencionada em todo o código-fonte em sua entidade separada; por exemplo, se você tiver um "nome" da string várias vezes mencionado no código-fonte, o Ruby precisará armazenar tudo isso em objetos String separados, porque eles pode mudar mais tarde (essa é a natureza de uma string Ruby).

Se você usar uma string como chave Hash, Ruby precisará avaliar a string e analisar seu conteúdo (e calcular uma função hash) e comparar o resultado com os valores (hash) das chaves que já estão armazenadas no Hash. .

Se você usa um símbolo como chave Hash, está implícito que é imutável, então Ruby pode basicamente fazer uma comparação entre a (função hash da) identificação de objeto e a identificação de objeto (com hash) de chaves que já estão armazenadas em o Hash. (muito mais rapido)

Desvantagem: cada símbolo consome um slot na tabela de símbolos do intérprete Ruby, que nunca é liberado. Os símbolos nunca são coletados no lixo. Portanto, uma caixa de esquina é quando você tem um grande número de símbolos (por exemplo, símbolos gerados automaticamente). Nesse caso, você deve avaliar como isso afeta o tamanho do seu intérprete Ruby.

Notas:

Se você fizer comparações de strings, o Ruby poderá comparar símbolos apenas pelos respectivos IDs de objeto, sem precisar avaliá-los. Isso é muito mais rápido do que comparar seqüências de caracteres, que precisam ser avaliadas.

Se você acessar um hash, o Ruby sempre aplicará uma função de hash para calcular uma "chave de hash" de qualquer chave que você usar. Você pode imaginar algo como um hash MD5. E então Ruby compara essas "chaves de hash" umas com as outras.

Resposta longa:

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/


5
Fyi, Symbols será GCd na próxima versão do Ruby: bugs.ruby-lang.org/issues/9634
Ajedi32

2
Além disso, Strings são congeladas automaticamente quando usadas como chaves Hash no Ruby. Portanto, não é exatamente verdade que Strings são mutáveis ​​quando se fala sobre elas nesse contexto.
precisa saber é o seguinte

1
Informações importantes sobre o tópico & Primeiro link na seção "Resposta longa" são removidas ou migradas.
Hbksagar

2
Símbolos são lixo coletado no Ruby 2.2
Marc-André Lafortune

2
Ótima resposta! Do lado dos trolls, sua "resposta curta" também é longa o suficiente. ;)
technophyle

22

O motivo é a eficiência, com vários ganhos sobre uma String:

  1. Os símbolos são imutáveis; portanto, a pergunta "o que acontece se a chave mudar?" não precisa ser solicitado.
  2. As cadeias de caracteres são duplicadas no seu código e normalmente ocupam mais espaço na memória.
  3. As pesquisas de hash devem calcular o hash das chaves para compará-las. Isto é O(n)para Strings e constante para Symbols.

Além disso, o Ruby 1.9 introduziu uma sintaxe simplificada apenas para hash com chaves de símbolos (por exemplo h.merge(foo: 42, bar: 6)), e o Ruby 2.0 possui argumentos de palavras - chave que funcionam apenas para chaves de símbolos.

Notas :

1) Você pode se surpreender ao saber que Ruby trata as Stringteclas de maneira diferente de qualquer outro tipo. De fato:

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

Apenas para chaves de seqüência de caracteres, Ruby usará uma cópia congelada em vez do próprio objeto.

2) As letras "b", "a" e "r" são armazenadas apenas uma vez para todas as ocorrências de :barum programa. Antes do Ruby 2.2, era uma má ideia criar constantemente novos Symbolsque nunca eram reutilizados, pois eles permaneceriam na tabela de pesquisa de símbolos global para sempre. O Ruby 2.2 irá coletá-los no lixo, então não se preocupe.

3) Na verdade, o cálculo do hash para um símbolo não demorou muito no Ruby 1.8.x, pois o ID do objeto era usado diretamente:

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

No Ruby 1.9.x, isso mudou conforme os hashes mudam de uma sessão para outra (incluindo as de Symbols):

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

+1 para suas excelentes notas! Inicialmente eu não mencionou a função hash na minha resposta, porque eu tentei torná-lo mais fácil de ler :)
Tilo

@Tilo: na verdade, é por isso que eu escrevi a minha resposta :-) I acabou de editar a minha resposta a mencionar a sintaxe especial no Ruby 1.9 e as prometidas parâmetros nomeados do Ruby 2.0
Marc-André Lafortune

Você pode explicar como as pesquisas de Hash são constantes para Símbolos e O (n) para Strings?
Asad Moosvi

7

Re: qual é a vantagem sobre o uso de uma string?

  • Estilo: é a maneira Ruby
  • Pesquisas de valor (muito) um pouco mais rápidas, pois hash de um símbolo é equivalente a hash de um número inteiro versus hash de uma string.

  • Desvantagem: consome um slot na tabela de símbolos do programa que nunca é liberado.


4
+1 por mencionar que o símbolo nunca é coletado como lixo.
Vortico 23/06

o símbolo nunca é coletado como lixo - não é verdade desde ruby ​​2.2+
eudaimonia 9/01

0

Eu ficaria muito interessado em acompanhar as seqüências congeladas introduzidas no Ruby 2.x.

Quando você lida com várias seqüências de caracteres provenientes de uma entrada de texto (estou pensando em parâmetros HTTP ou carga útil, através do Rack, por exemplo), é muito mais fácil usar seqüências de caracteres em qualquer lugar.

Quando você lida com dezenas deles, mas eles nunca mudam (se é o "vocabulário" da sua empresa)), gosto de pensar que congelá-los pode fazer a diferença. Ainda não fiz nenhum benchmark, mas acho que isso fecharia o desempenho dos símbolos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.