String結合のパフォーマンス

Javaでは「Stringの結合にはStringBufferを使え」と言われる.これは間違いではないが,大きく誤解されている部分でもある.Stringの結合においてStringBufferを使うべきシーンは実はそれほど多くはない.むしろ素直にStringの結合を使った方が記述性,可読性,パフォーマンスにおいて優れている例は珍しくない.

例えば次のような例を考える.

例1:
String str = "文字列1"+ i+"文字列2"+j+"文字列3"
例2:
StringBuffer sBuffer = new StringBuffer();
sBuffer.append("文字列1");
sBuffer.append(i);
sBuffer.append("文字列2");
sBuffer.append(j);
sBuffer.append("文字列3");
String str = new String( sBuffer );
例3
String str = "文字列1" 
	   + "文字列2" 
	   + "文字列3"
例4
String str = "文字列1文字列2文字列3"

例1のような場合はコンパイル時に内部的にStringBufferに展開され,例2と等価なものになるので特に問題はない.

例3は通常コンパイル時に結合され,クラスファイルレベルでは例4と等価な形になるため全く問題がない.(こういうものは"javap -c クラスファイル名"で逆アセンブルすれば確認できる.)むしろ例2のようにStringBufferを使うよりはコンパクト且つ高速になる.(StringBufferの方もHotSpotVM等による実行時最適化で例3と同等の性能になる可能性はある.) 例3は非常に長くて読み難い文字列を適当な長さで改行するためにも利用できる.例1-4においては例2のようにStringBufferを使って書く理由は全くない.

次に以下のような例について考える.

例5
String str = "文字列1" ;
for( i=0 ; i< max ; i++){
    str = str+ i + "文字列2";
}
str = str+j+"文字列3";
例6
String str = "文字列1" ;
if( x < 10 ){
    str = str+ i + "文字列2";
}
if( y > 30 ){
    str = str+j+"文字列3";
}

例5のような場合で,maxが極めて大きい場合はこれがボトルネックになることもある.こういう場合にはStringBuffer(JDK1.5以後はStringBuilder)の使用が強く推奨される.例6のような場合もStringBufferを使った方が早くはなるだろう.条件分岐が多い場合は最初からStringBufferを使うことが推奨される.

しかしこれらの場合でもmaxが小さい場合や使われる回数が少なくパフォーマンス的に大きな問題でない場合は必ずしも書き換える必要はない.あくまでStringBufferの使用が推奨されるだけで,そうでない場合でも致命的トラブルになる可能性はかなり小さい.最近は下手をすると例5や6のような場合でさえもJVMが最適化してしまい「問題が発生しなくてびっくり!」なんてこともあるかもしれない.だが何も好きこのんで危ない橋を渡ることもないので,まともなプログラマーならばStringBuffer/StringBuilderを使うことをお奨めする.

「文字列の結合にはStringBufferを使え」というのは,本来は例5や例6のような場合限定の話だったのだが,入門書に孫引きされる間にいつのまにか「全ての場合に」と誤解されるようになった.

まとめると例5や例6の時に

  • maxが大きくなったり,多数の条件分岐があることが予想される.
  • パフォーマンスに注意する必要があるアプリである.
  • この部分がパフォーマンス上のボトルネックになる可能性が少なくない.
  • StringBuffer/StringBuilderの使用により可読性が損なわれない.

という場合は,(そしておそらくはそのような場合にのみ,)StringよりStringBuffer/StringBuilderを使用すべきである.それ以外の場合ならばどちらで書いても大きな問題にはならない.