Perché usare i simboli come chiavi hash in Ruby?


162

Molte volte le persone usano i simboli come chiavi in ​​un hash Ruby.

Qual è il vantaggio rispetto all'utilizzo di una stringa?

Per esempio:

hash[:name]

vs.

hash['name']

Risposte:


227

TL; DR:

L'uso dei simboli non solo consente di risparmiare tempo durante i confronti, ma consente anche di risparmiare memoria, poiché vengono memorizzati solo una volta.

I simboli di Ruby sono immutabili (non modificabili), il che rende molto più facile la ricerca di qualcosa

Risposta breve (ish):

L'uso dei simboli non solo consente di risparmiare tempo durante i confronti, ma consente anche di risparmiare memoria, poiché vengono memorizzati solo una volta.

I simboli in Ruby sono fondamentalmente "stringhe immutabili" . Ciò significa che non possono essere modificati e implica che lo stesso simbolo, quando viene fatto riferimento più volte nel codice sorgente, viene sempre memorizzato come la stessa entità, ad esempio ha lo stesso ID oggetto .

Le stringhe invece sono mutabili , possono essere cambiate in qualsiasi momento. Ciò implica che Ruby deve archiviare ogni stringa menzionata nel codice sorgente in un'entità separata, ad esempio se si dispone di una stringa "nome" più volte menzionata nel codice sorgente, Ruby deve archiviarle tutte in oggetti String separati, poiché esse potrebbe cambiare in seguito (questa è la natura di una stringa Ruby).

Se usi una stringa come chiave hash, Ruby deve valutare la stringa e guardarne il contenuto (e calcolare una funzione hash su quella) e confrontare il risultato con i valori (hash) delle chiavi che sono già memorizzate nell'hash .

Se usi un simbolo come chiave hash, è implicito che è immutabile, quindi Ruby può fondamentalmente fare un confronto tra la (funzione hash dell'id oggetto) e l'id (hash) degli oggetti delle chiavi che sono già memorizzati in l'hash. (più veloce)

Unico inconveniente: ogni simbolo consuma uno slot nella tabella dei simboli dell'interprete Ruby, che non viene mai rilasciato. I simboli non vengono mai raccolti nella spazzatura. Quindi un caso angolare è quando hai un gran numero di simboli (ad esempio quelli generati automaticamente). In tal caso, dovresti valutare in che modo influisce sulla dimensione del tuo interprete Ruby.

Appunti:

Se si effettuano confronti di stringhe, Ruby può confrontare i simboli semplicemente per i loro ID oggetto, senza doverli valutare. È molto più veloce rispetto al confronto di stringhe, che devono essere valutate.

Se accedi a un hash, Ruby applica sempre una funzione hash per calcolare una "chiave hash" da qualunque chiave tu usi. Puoi immaginare qualcosa come un hash MD5. E poi Ruby confronta quelle "chiavi con hash" una con l'altra.

Risposta lunga:

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
Cordiali saluti, Symbols sarà GCd nella prossima versione di Ruby: bugs.ruby-lang.org/issues/9634
Ajedi32

2
Inoltre, le stringhe vengono automaticamente congelate quando utilizzate come chiavi hash in Ruby. Quindi non è esattamente vero che le stringhe sono mutabili quando ne parlano in questo contesto.
Ajedi32,

1
Le informazioni dettagliate sull'argomento e il primo collegamento nella sezione "Risposta lunga" vengono rimosse o migrate.
Hbksagar,

2
I simboli sono rifiuti raccolti in Ruby 2.2
Marc-André Lafortune,

2
Bella risposta! Per quanto riguarda la pesca a traina, anche la tua "risposta breve" è abbastanza lunga. ;)
tecnophyle

22

Il motivo è l'efficienza, con più guadagni su una stringa:

  1. I simboli sono immutabili, quindi la domanda "cosa succede se la chiave cambia?" non ha bisogno di essere chiesto.
  2. Le stringhe sono duplicate nel codice e in genere occupano più spazio in memoria.
  3. Le ricerche hash devono calcolare l'hash delle chiavi per confrontarle. Questo è O(n)per stringhe e costante per simboli.

Inoltre, Ruby 1.9 ha introdotto una sintassi semplificata solo per l'hash con i simboli (ad es. h.merge(foo: 42, bar: 6)) E Ruby 2.0 ha argomenti per le parole chiave che funzionano solo per i simboli.

Note :

1) Potresti essere sorpreso di apprendere che Ruby tratta le Stringchiavi in ​​modo diverso rispetto a qualsiasi altro tipo. Infatti:

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

Solo per le chiavi di stringa, Ruby utilizzerà una copia bloccata anziché l'oggetto stesso.

2) Le lettere "b", "a" e "r" sono memorizzate una sola volta per tutte le occorrenze di :barun programma. Prima di Ruby 2.2, era una cattiva idea creare costantemente nuovi Symbolsche non fossero mai riutilizzati, poiché sarebbero rimasti per sempre nella tabella di ricerca globale di Symbol. Ruby 2.2 li raccoglierà spazzatura, quindi non preoccuparti.

3) In realtà, calcolare l'hash per un simbolo non ha richiesto tempo in Ruby 1.8.x, poiché l'ID oggetto è stato usato direttamente:

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

In Ruby 1.9.x, questo è cambiato quando gli hash cambiano da una sessione all'altra (inclusi quelli di Symbols):

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

+1 per le tue note eccellenti! Inizialmente non ho menzionato la funzione hash nella mia risposta, perché ho cercato di rendere più facile la lettura :)
Tilo,

@Tilo: in effetti, è per questo che ho scritto la mia risposta :-) Ho appena modificato la mia risposta per menzionare la sintassi speciale in Ruby 1.9 e i parametri nominati promessi di Ruby 2.0
Marc-André Lafortune

Puoi spiegare come le ricerche Hash sono costanti per i simboli e O (n) per le stringhe?
Asad Moosvi,

7

Ri: qual è il vantaggio rispetto all'utilizzo di una stringa?

  • Styling: è la via del rubino
  • Ricerche di valori (molto) leggermente più veloci poiché l'hashing di un simbolo equivale all'hash di un numero intero rispetto all'hash di una stringa.

  • Svantaggio: consuma uno slot nella tabella dei simboli del programma che non viene mai rilasciato.


4
+1 per menzionare che il simbolo non viene mai raccolto.
Vortico,

il simbolo non è mai spazzatura raccolta - non vero dal rubino 2.2+
eudaimonia

0

Sarei molto interessato a un follow-up per quanto riguarda le stringhe congelate introdotte in Ruby 2.x.

Quando hai a che fare con numerose stringhe provenienti da un input di testo (sto pensando a parametri HTTP o payload, ad esempio tramite Rack), è molto più facile usare le stringhe ovunque.

Quando hai a che fare con dozzine di loro ma non cambiano mai (se sono il "vocabolario" della tua attività), mi piace pensare che congelarli possa fare la differenza. Non ho ancora fatto alcun benchmark, ma suppongo che sarebbe vicino alla performance dei simboli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.