文字列連結のスピード
via オレンジニュース
- StringBuilder と += による文字列連結の速度比較 @ Landscape - エンジニアのメモ
同じ事をJavaで試してみました。
環境は、
+=演算子による連結、String#concat()メソッドによる連結(あまり一般的ではないと思いますが・・・)、定石のStringBufferクラス、さらにJ2SE 5.0で加わったStringBuilderクラスも試してみました。
ソースはこれ。J2SE 5.0以降対応です。5.0の新しいSystem.nanoTime()はこういうベンチマークで便利なんですが、DateFormat等で整形できないのがなんとも・・・。
public class StringConcatenateTest { public static void main(String[] args) { int times = 1000; System.out.println(times + " times loop."); // += long start = System.nanoTime(); String str = ""; for (int i = 0; i < times; i++) { str += i; } System.out.println("String += : " + getNanoTimeString(System.nanoTime() - start)); // String#concat() long start_concat = System.nanoTime(); String str_concat = ""; for (int i = 0; i < times; i++) { str_concat.concat("" + i); } System.out.println("String#concat() : " + getNanoTimeString(System.nanoTime() - start_concat)); // StringBuffer long start_stringbuffer = System.nanoTime(); StringBuffer buffer = new StringBuffer(); for (int i = 0; i < times; i++) { buffer.append(i); } System.out.println("StringBuffer : " + getNanoTimeString(System.nanoTime() - start_stringbuffer)); // StringBuilder long start_stringbuilder = System.nanoTime(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < times; i++) { builder.append(i); } System.out.println("StringBuilder : " + getNanoTimeString(System.nanoTime() - start_stringbuilder)); } private static String getNanoTimeString(long nanoTime) { long t = nanoTime; long nsec = t % 1000; t /= 1000; long usec = t % 1000; t /= 1000; long msec = t % 1000; t /= 1000; long sec = t % 60; t /= 60; long minute = t % 60; t /= 60; long hour = t; return String.format("%02d:%02d:%02d.%03d%03d%03d", hour, minute, sec, msec, usec, nsec); } }
まずは1000回のループ。
1000 times loop. String += : 00:00:00.038828399 String#concat() : 00:00:00.012665577 StringBuffer : 00:00:00.000677181 StringBuilder : 00:00:00.000211759
1万回では、
10000 times loop. String += : 00:00:05.104651569 String#concat() : 00:00:00.029140576 StringBuffer : 00:00:00.005066845 StringBuilder : 00:00:00.002816839
10万回
100000 times loop. String += : (話にならないくらい遅い) String#concat() : 00:00:00.112415405 StringBuffer : 00:00:00.108727785 StringBuilder : 00:00:00.101025968
100万回
1000000 times loop. String += : (話にならないくらい遅い) String#concat() : 00:00:00.873067666 StringBuffer : 00:00:00.669204047 StringBuilder : 00:00:00.593941409
1000万回
10000000 times loop. String += : (話にならないくらい遅い) String#concat() : 00:00:08.593192710 StringBuffer : (java.lang.OutOfMemoryErrorで実行できず) StringBuilder : (java.lang.OutOfMemoryErrorで実行できず)
結論。StringBuilder激速。これからはStringBuilder。ただし、スレッドセーフでない点には気をつけましょう。StringBufferとStringBuilderの関係は、VectorとArrayList、HashtableとHashMapの関係と同じということですね。
あと、C#のStringBuilderはスレッドセーフなので、混同しないように。
String#concat()が意外と速いのは驚きでした。メモリ消費も少そうな感じですね。(id:hyperash:20051205で修正版を書きました)
追記
こんな情報も見つけてしまいました。またまた悩ましい・・・。
- パフォーマンスの迷信: "a" + "b" 対 StringBuffer("a").append("b") @ ブログ: 岡崎 - Okazaki's blog
個人的には、このような些細なパフォーマンスアップテクニックを身につけるよりは、ソースコードの可読性を向上させ、パフォーマンスチューニングはコンパイラに任せるというスタンスがやはり正しいでしょうし、特に最近はコンパイラに任せきりでも十分なような気がします(状況によりますが・・ ;-<
その「状況による」チューニングばかりしている今日この頃 :-(
2005/12/04 追記
コメント欄で指摘を頂きまして、String#concat()の使い方を激しく間違っておりました。ベンチマークやり直しですね。
2005/12/05 追記
id:hyperash:20051205で、ベンチマークをやり直した結果を書きました。