String は本当に遅いのか?
Java の世界では「String で文字列連結するな! StringBuffer を使え!」とよく言われます。これは String が不変オブジェクトであることに起因します。これは直感的には理解できますが、実際のところどうなんでしょう。
というわけで、実際に String/StringBuffer/StringBuilder の文字列連結速度を測定してみました。
- 各クラスの特徴
クラス名 | 不変/可変 | synchronized |
String | 不変 | |
StringBuffer | 可変 | する |
StringBuilder | 可変 | しない |
- 実験用ソースコード
public class StringTest { private static final int LOOP_MAX = 1000; private static long execString() { long startTime = System.currentTimeMillis(); // Begin String str = new String(); for (int i = 0; i < LOOP_MAX; i++) { str += i; } // End long finishTime = System.currentTimeMillis(); return finishTime - startTime; } private static long execStringBuffer() { long startTime = System.currentTimeMillis(); // Begin StringBuffer str = new StringBuffer(); for (int i = 0; i < LOOP_MAX; i++) { str.append(i); } str.toString(); // End long finishTime = System.currentTimeMillis(); return finishTime - startTime; } private static long execStringBuilder() { long startTime = System.currentTimeMillis(); // Begin StringBuilder str = new StringBuilder(); for (int i = 0; i < LOOP_MAX; i++) { str.append(i); } str.toString(); // End long finishTime = System.currentTimeMillis(); return finishTime - startTime; } public static void main(String[] args) { for (int i = 1; i <= 3; i++) { System.out.println("--- " + i + "回目 ---"); System.out.println("String : " + execString()); System.out.println("StringBuffer : " + execStringBuffer()); System.out.println("StringBuilder : " + execStringBuilder()); System.out.println(""); } } }
- 測定結果 (ループ 1000 回)
--- 1回目 --- String : 15 StringBuffer : 0 StringBuilder : 0 --- 2回目 --- String : 0 StringBuffer : 0 StringBuilder : 0 --- 3回目 --- String : 16 StringBuffer : 0 StringBuilder : 0
確かに String は遅そうですね。
- 測定結果 (ループ 10000 回)
--- 1回目 --- String : 703 StringBuffer : 0 StringBuilder : 0 --- 2回目 --- String : 687 StringBuffer : 0 StringBuilder : 0 --- 3回目 --- String : 594 StringBuffer : 0 StringBuilder : 0
String の遅さが顕著になっています。文字列連結のたびにオブジェクト生成してるんだから当然といえば当然か。
一方、StringBuffer と StringBuilder に差はまったくありません。10000回程度の append じゃ話にならんか。
- 測定結果 (ループ 100000 回)
--- 1回目 --- StringBuffer : 31 StringBuilder : 16 --- 2回目 --- StringBuffer : 31 StringBuilder : 15 --- 3回目 --- StringBuffer : 32 StringBuilder : 15
String は遅すぎるので、測定対象から外しました。
synchronized していない分、StringBuilder のほうが倍くらい早い。ところで、synchronized のスペルをいつも忘れるのは俺だけでしょうか。