北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

文字列の誘惑

perl上がりだと、「.」演算子と同じ感覚で文字列をくっつけたくなるけど、ちょっと待った!

例えば、0〜9の数字を1万個つなげたいと思ってコードを書くと、以下のようになると思います。

String str = "";

for(int i = 0; i < 10000; i++){
    str += (i % 10);
}

でも、このコードの実行は0.4秒もかかります。連結数が増すとさらに状況は悪化し、20000個だと2秒、30000個だと7秒*1もかかってしまいます。

これは、「+=」の挙動のせいです。見た目的にstrオブジェクトに文字列を加えるように見えますが、違います。まず、str = str + (i % 10)であることと、もう一つ、「+」演算は新たなオブジェクトを生成すると言う事実が効いてきます。strが5000バイトだとすると、足し算をすると新たに5001バイトのオブジェクトがメモリ上に生成されるわけです。さらにstrが500Mだと・・・想像するのも嫌( ´△`)アァ-。

と言うわけで、文字列の足し算は効率がよくないと言うことを踏まえた上で、パフォーマンス上必要な時には迷わずにStringBufferを使うべきかと。で、StringBufferはStringを継承してないので、toString()を呼んで文字列に戻しておくとよさげです。

String str = null;
StringBuffer buff = new StringBuffer();
for(int i = 0; i < 10000; i++){
    buff.append(i % 10);
}
str = buff.toString();


追記。では、演算子オーバーロードのないjavaで、なぜ文字列は「+」が使えるんでしょう? javadocによるとコンパイラが頑張ってるようです。

文字列バッファは、バイナリ文字列の連結演算子 + を実装するためにコンパイラで使用されます。たとえば、次のコード

x = "a" + 4 + "c"

は、以下の同等なコードとしてコンパイルされます。

x = new StringBuffer().append("a").append(4).append("c")
.toString()

結局はStringBufferなわけですね。

*1:StringBufferの実装だと0.013秒です。何倍??