プログラミングでは既存の文字列に新しい文字列を追加する処理が頻繁に発生します。Rubyでは<<演算子や+=演算子を使って変数に文字列を追加できます。この記事では文字列追加の方法とそれぞれの違い、パフォーマンスの観点での使い分けを解説します。
基本的な使い方
<< 演算子で文字列を追加
<<演算子は元の文字列オブジェクトに直接文字列を追加します。新しいオブジェクトを作らないため、メモリ効率が良いのが特徴です。
text = 'Hello'
puts text.object_id
text << ' World'
puts text
puts text.object_id # 同じオブジェクトID60
Hello World
60<<演算子を使うと、オブジェクトIDが変わらないことがわかります。これは元の文字列オブジェクトが直接変更(破壊的変更)されていることを意味します。
+= 演算子で文字列を追加
+=は新しい文字列オブジェクトを生成して変数に再代入します。元のオブジェクトは変更されません。
text = 'Hello'
puts text.object_id
text += ' World'
puts text
puts text.object_id # 異なるオブジェクトID60
Hello World
80+=を使うとオブジェクトIDが変わります。これはtext = text + ' World'と同じ意味で、新しい文字列オブジェクトが生成されて変数に代入されています。
破壊的変更と非破壊的変更の違い
この違いは、同じオブジェクトを複数の変数が参照している場合に重要になります。
# << の場合(破壊的)
a = 'Hello'
b = a
a << ' World'
puts "a: #{a}"
puts "b: #{b}" # bも変わる!
puts '---'
# += の場合(非破壊的)
c = 'Hello'
d = c
c += ' World'
puts "c: #{c}"
puts "d: #{d}" # dは変わらないa: Hello World
b: Hello World
---
c: Hello World
d: Hello<<は元のオブジェクトを変更するため、同じオブジェクトを参照しているbにも影響が及びます。一方、+=は新しいオブジェクトを作るため、dは元の値のままです。この挙動の違いはバグの原因になりやすいため、しっかり理解しておきましょう。
実践的な使い方
ループで文字列を組み立てる
ループ内で文字列を繰り返し追加する場合は、パフォーマンスの観点から<<が推奨されます。
# HTMLテーブルを組み立てる例
items = ['りんご', 'バナナ', 'みかん']
html = ''
items.each do |item|
html << "- #{item}
"
end
html << '
'
puts html- りんご
- バナナ
- みかん
concatメソッド
concatは<<と同じ破壊的な動作をしますが、複数の引数を渡せるのが利点です。
greeting = 'Hello'
greeting.concat(', ', 'World', '!')
puts greetingHello, World!大量の文字列追加を行う場合、<<は+=よりも高速です。+=は毎回新しいオブジェクトを生成するため、ループ内で使うとメモリ消費が増えます。ループ内では<<を使いましょう。
freezeされた文字列や、# frozen_string_literal: trueが有効なファイルでは<<がエラーになります。この場合は+=を使うか、String.newで可変な文字列を作成してください。
まとめ
<<は元のオブジェクトを直接変更する(破壊的・高速)+=は新しいオブジェクトを生成して再代入する(非破壊的・安全)- 同じオブジェクトを複数変数で参照している場合、
<<は全変数に影響する - ループ内での文字列組み立てには
<<が推奨 concatは<<と同じ動作で複数引数を受け取れる