web-dev-qa-db-ja.com

Rubyでシンボルをハッシュキーとして使用する理由

多くの場合、人々はRubyハッシュのキーとしてシンボルを使用します。

文字列を使用することの利点は何ですか?

例えば。:

hash[:name]

vs.

hash['name']
153
user979912

TL; DR:

シンボルを使用すると、比較を行うときに時間を節約できるだけでなく、一度しか保存されないため、メモリも節約できます。

Rubyシンボルは不変(変更不可)であり、何かを簡単に調べることができます

短い(ish)答え:

シンボルを使用すると、比較を行うときに時間を節約できるだけでなく、一度しか保存されないため、メモリも節約できます。

Rubyの記号は、基本的に "不変文字列"です。これは、変更できないことを意味し、ソースコード全体で何度も参照される場合、同じシンボルが常に同じエンティティとして格納されることを意味します。同じオブジェクトIDを持ちます。

一方、文字列は可変です、いつでも変更できます。これは、Rubyが、ソースコード全体で言及した各文字列を個別のエンティティ(たとえば、ソースコードで文字列「名前」が複数回言及されている場合、Rubyは後で変更される可能性があるため、これらをすべて個別のStringオブジェクトに格納する必要があります(Ruby文字列の性質です) 。

文字列をハッシュキーとして使用する場合、Rubyは文字列を評価してその内容を調べ(そのハッシュ関数を計算)、結果を既にあるキーの(ハッシュされた)値と比較する必要がありますハッシュに保存されます。

シンボルをハッシュキーとして使用する場合、それは不変であることが暗黙的であるため、Rubyは基本的に、オブジェクトID(のハッシュ関数)とキーの(ハッシュされた)オブジェクトIDの比較を行うことができます。すでにハッシュに保存されています。 (はるかに高速)

欠点:各シンボルは、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

211
Tilo

その理由は、文字列に対して複数のゲインが得られる効率です。

  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のすべての出現に対して1回だけ保存されます。 Ruby 2.2より前は、常に再利用されない新しいSymbolsを作成することは悪い考えでした。これらはグローバルシンボルルックアップテーブルに永久に残るためです。 Ruby 2.2はガベージコレクションを行うため、心配はありません。

3)実際には、オブジェクトIDが直接使用されたため、Symbolのハッシュの計算はRuby 1.8.xで時間がかかりませんでした。

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

Ruby 1.9.xでは、ハッシュが1つのセッションから別のセッション(Symbolsのハッシュを含む)に変更されると、これが変更されました。

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

再:文字列を使用することの利点は何ですか?

  • スタイリング:そのRubyの方法
  • (非常に)シンボルのハッシュは整数のハッシュと文字列のハッシュに等しいため、値の検索はわずかに高速です。

  • 欠点:プログラムのシンボルテーブル内の、決して解放されないスロットを消費します。

7
Larry K

Ruby 2.xで導入されたフリーズ文字列に関するフォローアップに非常に興味があります。

テキスト入力からの多数の文字列を処理する場合(たとえば、Rackを介してHTTPパラメーターまたはペイロードを考えています)、どこでも文字列を使用する方が簡単です。

あなたがそれらの数十を扱うが、それらが決して変わらないとき(それらがあなたのビジネス「語彙」であるならば)、私はそれらを凍結することが違いを生むことができると思うのが好きです。まだベンチマークを行っていませんが、シンボルのパフォーマンスに近いと思います。

0
jlecour