【Java】文字列を10万回連結してみた【String vs StringBuilder】

f:id:shogonir:20180204132011p:plain

 

目次

  1. この記事の目的
  2. 文字列連結の2種類の仕方
  3. パフォーマンスを比較する
  4. 10万回ではなく1000回であれば
  5. 結論

 

1. この記事の目的

Javaで文字列を連結する際は、StringStringBuilderのどちらを使えばいいのかを考えたいと思います。
StringStringBuilder の2つのクラスについては色々な主張を見かけます。例えば、
「文字列の連結を何回も行う時は String ではなく StringBuilder を使え」
「Java9からは最適化の方法が変わったからString使っていい」
などです。実際どっちが速いのか実験してみました。

サンプルコードをGitHubにあげました。
Dockerがある環境で python execute_multi.py で実行してください。

 

github.com

 

2. 文字列連結の2種類の仕方

StringStringBuilder の文字列の連結の方法を紹介します。
まずは String から、こちらはおなじみかと思います。

 

public class StringConcatWithOperator {

    public static void main (String[] args) {
        
        long startTime = System.currentTimeMillis();

        String message = "";

        for (int i = 0; i < 100 * 1000; i++) {
            message += "a";
        }
        
        long endTime = System.currentTimeMillis();

        System.out.println("    " + (endTime - startTime) + "ms");
    }
}

 

String+ 演算子で文字列を連結しています。
この時、毎回 String のオブジェクトを作成しては捨てています。
このオブジェクトの作成にコストがかかるので遅くなります。

続いて StringBuilder です。

 

public class StringConcatWithBuilder {

    public static void main (String[] args) {
        
        long startTime = System.currentTimeMillis();

        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < 100 * 1000; i++) {
            builder.append("a");
        }

        String message = builder.toString();
        
        long endTime = System.currentTimeMillis();

        System.out.println("    " + (endTime - startTime) + "ms");
    }
}

 

StringBuilderappend で文字列を連結しています。
この時は "a" のオブジェクトも使い回しですし、 StringBuilderインスタンスも1つだけです。

さて、パフォーマンスの差はどのようになるのでしょうか。

 

3. パフォーマンスを比較する

実際に文字列を10万回連結した際の速度をJava8とJava9で比較しました。

 

バージョン String StringBuilder
Java8 10263ms 9ms
Java9 1862ms 13ms

 

ここから分かったことを2つまとまます。

 

  • String より StringBuilder の方が圧倒的に速い
  • String の連結の最適化がJava8から9でかなり良くなっている

 

まず StringBuilder の方が速い件は、先述のオブジェクトの使い回しが原因です。

String の連結の最適化については、他の記事によいものがありますので探してみてください。

 

4. 10万回ではなく1000回であれば

先ほどの実験では10万回文字列を連結しましたが、正直少し現実的ではありません。
そこで、1000回ぐらいだと String の方が速いんじゃないの?ということでこちらも実験します。

結果は以下の通りです。

 

バージョン String StringBuilder
Java8 5ms 1ms
Java9 121ms 1ms

 

ここから分かったことを2つまとまます。

 

  • 相変わらず StringBuilder の方がはやいが、Java8ではほぼ差はない
  • String の連結はJava9よりJava8の方がはやい

 

Java9の最適化はDynamicInvokeという方法らしいのですが、文字列の数が多くなるほど効くようです。

 

5. 結論

結論は、「場合による」。

まず、数万以上の文字列を連結を行う場合は StringBuilder を使うべきです。
100分の1程度の時間で文字列の連結を行えるからです。

千程度の文字列を連結する際も、 StringBuilder の方が速いは速いです。
アプリやゲームなどで、1フレームで使える時間が限られている場合は StringBuilder がいいです。
Java9だと100ms以上かかる場合もあるからです。
そこまでパフォーマンスにこだわらない場合は、プロジェクトメンバーの技術力や可読性と相談です。