Rubyのシンボルを詳しくまとめてみた。
Rubyのコードを読んでいると、
{ 'key1' => 'value1', 'key2' => 'value2' }
{ :key1 => 'value1', :key2 => 'value2' }
{ key1: 'value1', key2: 'value2' }
のように、同じような記述が書かれていて、混乱することはないだろうか。
そもそも、2行目のkeyの前に付いている、:って何だ?って思う人もいるのではないか。
: が先頭についてる、単語(2行目の :key 等)をRubyでは、シンボルという。
シンボルの特徴
1.ハッシュのキーに使われる
シンボルは、ハッシュのキーによく使われる。
**キーにシンボルが使われているハッシュ**
{ :key1 => 'value1', :key2 => 'key2'}
そして、「キーにシンボルが使われているハッシュ」には、より簡単な記法があり、コードをより読みやすくすることができる。
**「キーがシンボルのハッシュ」の簡略記法**
#「キーがシンボルのハッシュ」と意味は全く同じ。 { hoge: 'hoge', fuga: 'fuga' }
2.文字列と違い、名前に一意性をもたらす
シンボルは、その名前に一意性を持たせることができる。すなわち同じ名前の場合、生成されるインスタンスは同じになる。
#同じ名前の文字列同士とシンボル同士から生成されるインスタンスを比較してみる hoge1 = "hoge" hoge2 = "hoge" hoge3 = :hoge hoge4 = :hoge #==は等値を表す論理演算子 puts hoge1 == hoge2 #=>true puts hoge3 == hoge4 #=>true #equal?は生成されたインスタンスが同じ場合、真を返す puts hoge1.equal?(hoge2) #=>false puts hoge3.equal?(hoge4) #=>true #同じ名前のシンボルのインスタンスが等しいことが分かる
また、プログラム内部で文字列を比較する場合、文字列に含まれる文字1文字単位で比較することになるが、シンボルを比較する場合、シンボル単位で比較することができる。シンボル単位での比較は、シンボルクラスのオブジェクトとして、振り分けられたobject_idをもとに行われる。
#同じ名前の文字列同士とシンボル同士のobject_idを比較してみる puts 'hoge'.object_id #=> 70261881731340 puts 'hoge'.object_id #=> 70261881731280 puts :hoge.object_id #=> 405448 puts :hoge.object_id #=> 405448 #同じ名前のシンボル同士はobject_idが等しいことが分かる
これによって、文字列同士の比較に比べ、シンボル同士の比較は格段にスピードが上がる。なぜなら、上記のhogeで比較するとき、文字列では、'h','o','g','e'と、4文字について比較しなければならないのに対し、シンボルでは、object_id(405448)のみを比較すればいいからである。
それでは、本文字列とシンボルではどれほど処理速度が変わってくるのだろうか。
実際に、調べてみた。
#キーが文字列のハッシュの値ひとつを1万回取得する時間 hash = {'key' => 'value'} start_time = Time.now 10000.times do |i| hash['key'] end end_time = Time.now hash_string = end_time - start_time puts hash_string #=> 0.002935 #キーがシンボルのハッシュの値ひとつを1万回取得する時間 hash = {:key => 'value'} start_time = Time.now 10000.times do |i| hash[:key] end end_time = Time.now hash_symbol = end_time - start_time puts hash_symbol #=> 0.000874 #キーがシンボルの場合の方が、3倍以上取得時間が速いことがわかる
さらに処理するデータが大きくなれば、それだけ処理速度にも差が出てくるのではないかと思い、調べてみた。
#100万個の異なる文字列のkey,とvalueのハッシュを並列に内包するハッシュから、ひとつのkeyのvalueにアクセスする時間 hash = {} 1000000.times do |i| hash.store('imp' + i.to_s, 'value' + i.to_s) end hash_string = [] 10.times do start_time = Time.now 100000.times do hash['imp1'] end end_time = Time.now hash_string.push end_time - start_time end puts hash_string.reduce(:+)/10 #=>0.0346474 #100万個の異なるシンボルのkey,とvalueのハッシュを並列に内包するハッシュから、ひとつのkeyのvalueにアクセスする時間 hash = {} 1000000.times do |i| hash.store(('imp' + i.to_s).intern, 'value' + i.to_s) end hash_symbol = [] 10.times do start_time = Time.now 100000.times do hash[:imp1] end end_time = Time.now hash_symbol.push end_time - start_time end puts hash_symbol.reduce(:+)/10 #=>0.0102569
処理速度は文字列よりシンボルを使った方が3倍に上がった。したがって、大きなデータを扱う場合も、シンボルをキーとする方が、処理速度が速いといえるだろう。
以上