Зачем использовать символы в качестве хеш-ключей в Ruby? - PullRequest
153 голосов
/ 19 ноября 2011

Часто люди используют символы в качестве ключей в хэше Ruby.

В чем преимущество использования строки?

например:.

hash[:name]

против

hash['name']

Ответы [ 4 ]

209 голосов
/ 19 ноября 2011

TL; DR:

Использование символов не только экономит время при выполнении сравнений, но и экономит память, поскольку они сохраняются только один раз.

Рубиновые символы являются неизменяемыми (не могут быть изменены), что значительно упрощает поиск чего-либо

Краткий (ish) ответ:

ИспользованиеСимволы не только экономят время при выполнении сравнений, но и экономят память, потому что они сохраняются только один раз.

Символы в Ruby в основном "неизменяемые строки" ... это означает, что они не могут быть изменены, и это означает, что один и тот же символ при многократном обращении по всему исходному коду всегда сохраняется как одна и та же сущность, например, имеет один и тот же идентификатор объекта.

С другой стороны, строки являются изменяемыми , их можно изменить в любое время.Это означает, что Ruby необходимо хранить каждую строку, которую вы упоминаете в исходном коде, в отдельной сущности, например, если у вас несколько раз упоминается строка «имя» в вашем исходном коде, Ruby необходимо хранить их все в отдельных объектах String, поскольку ониможет измениться позже (такова природа строки Ruby).

Если вы используете строку в качестве ключа Hash, Ruby необходимо оценить строку и посмотреть ее содержимое (и вычислить хеш-функцию для этого)и сравните результат со значениями (хэшированными) ключей, которые уже хранятся в хэше.

Если вы используете символ в качестве ключа хэша, это подразумевает, что он неизменен, поэтому Ruby может просто выполнитьсравнение (хеш-функции) идентификатора объекта с (хэшированными) идентификаторами объектов ключей, которые уже хранятся в хеше.(намного быстрее)

Недостаток: Каждый символ занимает ячейку в таблице символов интерпретатора Ruby, которая никогда не освобождается.Символы никогда не собираются мусором.Так что угловой случай - это когда у вас есть большое количество символов (например, автоматически сгенерированных).В этом случае вы должны оценить, как это влияет на размер вашего интерпретатора Ruby.

Примечания:

Если вы выполняете сравнение строк, Ruby может сравнивать символы только по их объекту.идентификаторы, без необходимости оценивать их.Это намного быстрее, чем сравнение строк, которые необходимо оценить.

Если вы обращаетесь к хешу, Ruby всегда применяет хеш-функцию для вычисления «хеш-ключа» из любого ключа, который вы используете.Вы можете представить что-то вроде MD5-хеша.И затем Руби сравнивает эти «хешированные ключи» друг с другом.

Длинный ответ:

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

21 голосов
/ 19 ноября 2011

Причина в эффективности, с несколькими коэффициентами усиления по строке:

  1. Символы неизменны, поэтому вопрос "что произойдет, если ключ изменится?" не нужно спрашивать.
  2. Строки дублируются в вашем коде и обычно занимают больше места в памяти.
  3. При поиске хеша необходимо вычислять хеш ключей для их сравнения. Это O(n) для строк и константа для символов.

Более того, в Ruby 1.9 введен упрощенный синтаксис только для хэша с символьными ключами (например, h.merge(foo: 42, bar: 6)), а в Ruby 2.0 есть аргументы ключевых слов , которые работают только для символьных ключей.

Примечания

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) Буквы "b", "a" и "r" сохраняются только один раз для всех вхождений :bar в программе. До Ruby 2.2 было плохой идеей постоянно создавать новые Symbols, которые никогда не использовались повторно, так как они навсегда останутся в глобальной таблице поиска Symbol. Ruby 2.2 будет собирать их мусор, так что не беспокойтесь.

3) На самом деле, вычисление хеша для Symbol в Ruby 1.8.x не занимало времени, так как идентификатор объекта использовался напрямую:

: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
7 голосов
/ 19 ноября 2011

Re: в чем преимущество по сравнению со строкой?

  • Стилирование: это Ruby-way
  • (Очень) немного более быстрое поиск значений после хеширования символаэквивалентно хешированию целого числа против хеширования строки.

  • Недостаток: занимает слот в таблице символов программы, который никогда не освобождается.

0 голосов
/ 24 июля 2014

Мне было бы очень интересно узнать, как будут заморожены строки, представленные в Ruby 2.x.

Когда вы имеете дело с многочисленными строками, поступающими из текстового ввода (я имею в виду HTTP-параметры илиполезная нагрузка, например, через Rack), гораздо проще использовать строки везде.

Когда вы имеете дело с десятками из них, но они никогда не меняются (если это ваш бизнес-словарь), мне нравится думатьчто замораживание их может иметь значение.Я еще не делал никаких тестов, но думаю, это будет близко к производительности символов.

...