ハッカーを目指す白Tのブログ

夏は白のTシャツを着ています。ラジオが好きです。(ラジオネーム: 隠れキリジダン)

Rubyのシンボルを詳しくまとめてみた。

Rubyのコードを読んでいると、

{ 'key1' => 'value1', 'key2' => 'value2' }
{ :key1 => 'value1', :key2 => 'value2' }
{ key1:  'value1', key2:  'value2' }

のように、同じような記述が書かれていて、混乱することはないだろうか。
そもそも、2行目のkeyの前に付いている、:って何だ?って思う人もいるのではないか。
: が先頭についてる、単語(2行目の :key 等)をRubyでは、シンボルという。

(3行目のハッシュの記法に関しては、シンボルの特徴の1で説明する)

シンボルの特徴

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倍に上がった。したがって、大きなデータを扱う場合も、シンボルをキーとする方が、処理速度が速いといえるだろう。




以上