Help us understand the problem. What is going on with this article?

RubyのStringとSymbolの違いとObject#freezeについて

More than 3 years have passed since last update.

ruby 2.3.0p0 (2015-12-25 revision 53290)で検証している。

StringとSymbolの違い

Stringクラスは文字列毎に新しくインスタンスを生成するが、Symbolクラスは同じ文字列の場合はインスタンスを生成しない。

検証する

Object#object_idを使って各オブジェクトを判別する

a = "test"
b = "test"

puts a.object_id # 70319641612700
puts b.object_id # 70319654222120

c = :test
d = :test

puts c.object_id # 356188
puts d.object_id # 356188

Object#equal?を使ってオブジェクトが同一であるか判別する

Object#equal?は、引数として与えられたオブジェクトがレシーバとなるオブジェクトと同じオブジェクトかどうかを判断する。

'test'.equal? 'test' # false
:test.equal? :test   # true

使い分け

ハッシュのキー等、何度も同じ文字を扱う場合は余分なインスタンスが生成されないのでSymbolを扱ったほうが若干早い。

いっぱい生成してみた

require 'benchmark'

Benchmark.bm 10 do |r|

  r.report 'symbol' do
    100000000.times { :test }
  end

  r.report 'string' do
    100000000.times { 'test' }
  end
end

結果はこんな感じ。

                 user     system      total        real
symbol       5.790000   0.040000   5.830000 (  5.885552)
string       8.970000   0.080000   9.050000 (  9.183865)

と、前述の通りsymbolの方がちょっとだけ早い。

何度か試してみたが誤差は大体±0.5秒程だった。

StringにObject#freezeを使った場合

freezeメソッドをつかってオブジェクトの状態を変更した時のオブジェクトIDはどうなるのか

'str'.freeze.equal? 'str'.freeze # true

a = 'str'
a.freeze.equal? a # true
b = 'str'
b.equal? b.freeze # true

c = 'str'
d = 'str'
c.freeze
d.freeze
c.frozen? # true
d.frozen? # true
c.equal? d # false

新たに生成したオブジェクトに対してfreezeを使うと毎回文字列オブジェクトを生成せず、常に同じオブジェクトを返している。

オーバーヘッドをちょっとでも小さくするためには文字列リテラルにfreezeをつけると良い。

ちなみに、Ruby2.3.0以降はfrozen_string_literal: trueというマジックコメントを入れることでデフォルトでStringオブジェクトはfreezeとなる。

# frozen_string_literal: true

"str".frozen? # => true
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away