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


161

Rubyハッシュのキーとして記号を使用することがよくあります。

文字列を使用するよりも優れている点は何ですか?

例えば:

hash[:name]

hash['name']

回答:


226

TL; DR:

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

Rubyシンボルは不変(変更できません)で、何かをはるかに簡単に検索できます

短い(おかしい)答え:

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

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

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

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

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

欠点: 各シンボルは、Rubyインタープリターのシンボルテーブルのスロットを消費します。これは解放されません。シンボルがガベージコレクションされることはありません。したがって、コーナーケースは、多数のシンボル(たとえば、自動生成されたもの)がある場合です。その場合は、これがRubyインタープリターのサイズにどのように影響するかを評価する必要があります。

ノート:

文字列比較を行う場合、RubyはオブジェクトIDだけでシンボルを比較できます。評価する必要はありません。これは、評価が必要な文字列を比較するよりもはるかに高速です。

ハッシュにアクセスすると、Rubyは常にハッシュ関数を適用して、使用するキーから「ハッシュキー」を計算します。MD5ハッシュのようなものを想像できます。そして、Rubyはそれらの「ハッシュされたキー」を互いに比較します。

長い答え:

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はRubyの次のバージョンでGCdされます:bugs.ruby-lang.org/issues/9634
Ajedi32

2
また、Rubyでハッシュキーとして使用される場合、文字列は自動的に凍結されます。したがって、この文脈で文字列について話すときに文字列が変更可能であることは正確には当てはまりません。
Ajedi32 2014

1
「長い回答」セクションのトピックと最初のリンクに関する優れた洞察が削除または移行されます。
Hbksagar 14

2
シンボルは、Ruby 2.2ガベージコレクションである
マルク・アンドレLafortune

2
正解です。トローリング側では、「短い答え」も十分に長いです。;)
テクノフィル'19

22

その理由は効率であり、文字列に対して複数の利点があります。

  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では、グローバルシンボルルックアップテーブルに永遠に残るため、再利用されないものを常に作成することは悪い考えでした。Ruby 2.2はそれらをガベージコレクションするので、心配ありません。

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

: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

優れたメモの+1!読みやすくするため、ハッシュ関数については最初は触れていませんでした:)
Tilo

@Tilo:私は私の答えを書いた理由:-)私はRuby 1.9の中に特殊な構文やRuby 2.0の約束の名前付きパラメータを言及するために私の答えを編集した確かに、だ
マルク=アンドレ・Lafortune

ハッシュルックアップがシンボルではどのように定数であり、文字列ではO(n)であるかを説明できますか?
Asad Moosvi 2017

7

再:文字列を使用するよりも利点は何ですか?

  • スタイリング:そのRubyの方法
  • (非常に)シンボルをハッシュすることは、整数をハッシュすることと文字列をハッシュすることと同じであるため、値の検索がわずかに速くなります。

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


4
+1は、シンボルがガベージコレクションされることは決してないことを言及した場合。
Vortico 2013年

シンボルがガベージコレクションされることはありません-ルビー2.2以降では当てはまりません
eudaimonia

0

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

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

それらを何十と扱ってもそれらが決して変わらない場合(それらがビジネスの「語彙」である場合)、それらをフリーズすることで違いが生じると思います。ベンチマークはまだ行っていませんが、シンボルのパフォーマンスに近いと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.