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 のスペルをいつも忘れるのは俺だけでしょうか。