Javaの文字列連結は速度を考慮して、「+」ではなく「StringBuffer」を使うべき

Java

今回は、一般的に言われているJavaの文字列連結について紹介します。


僕は都内でSIerをやっているのですが、新人時代のJava研修で教わったことがあります。


 

文字列連結する場合は「+」演算子を使わずに「StringBuffer」を使え!

 


Java研修の時は”へー。そーなんだ。”くらいでしか考えていませんでした。

理由は性能(スピード)だと教えてもらったかと思いますが、ちゃんと理解できず。。。です。

「百聞を一見し如かず」ということで、実際に検証してみようと思います。


環境情報

実際にJavaのプログラムを作って、僕のローカルマシンで動かす形で検証します。

ローカルマシンのスペックは以下になります。

  • OS:Windows7 Professional
  • CPU:Inter Pentium 2.3GHz
  • メモリ:4.00 GB
  • Java:1.7.0_02

あんまりいいパソコン使ってないんですよね。

でもまあ、検証するには十分な環境かと思います。

「+」演算子で文字列をする

まずは、「+」演算子で文字列を連結した場合のプログラムを作ります。

実際に作成したプログラムは以下となります。

“add+数字”の文字列追加を5万回繰り返すプログラムです。

全て、「+」演算子で文字列を連結しています。

import java.text.SimpleDateFormat;
import java.util.Date;

public class PlusConnect {
     public static void main(String[] args){
        System.out.print("start\r\n");

        //-- 日付フォーマット作成 --//
        SimpleDateFormat fmt = 
            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        String str = "";

        //-- 文字列の連携を指定回数だけ実行 --//
        for (int lc = 0; lc < 50000; lc++) {
            str = str + "add" + Integer.toString(lc);

            //-- 一定の間隔でログ出力 --//
            if (lc % 10000 == 0) {
                Date now = new Date();
                System.out.print(fmt.format(now) + 
                ":" + Integer.toString(lc) + 
                "回目のループ終了\r\n");
            }
        }

        //-- 文字列長を表示して終了 --//
        System.out.print(
            "文字列長=" + Integer.toString(str.length()));
        System.out.print("end\r\n");
     }
}

上記プログラムの実行結果は以下になります。

hoge>java -Xms256M -Xmx256m PlusConnect
start
2019/04/28 21:43:53.487:0回目のループ終了
2019/04/28 21:43:55.066:10000回目のループ終了
2019/04/28 21:43:59.256:20000回目のループ終了
2019/04/28 21:44:05.239:30000回目のループ終了
2019/04/28 21:44:12.774:40000回目のループ終了
文字列長=388890
end

コメントが解り辛くて申し訳ないです。

「0回目のループ終了」が開始日時で、「40000回目のループ終了」が終了日時です。

約19秒経過していることがわかります。


「StringBuffer」で文字列を連結する

次に、「StringBuffer」で文字列を連結した場合のプログラムを作ります。

実際に作成したプログラムは以下となります。

「StringBuffer」の「append」を使用して文字列を連結するプログラムです。

「+」演算子と同様に、文字列追加を5万回繰り返すプログラムです。

import java.text.SimpleDateFormat;
import java.util.Date;

public class BufferConnect {
    public static void main(String[] args){
        System.out.print("start\r\n");

        //-- 日付フォーマット作成 --//
        SimpleDateFormat fmt = 
            new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
        StringBuffer buf = new  StringBuffer();

        //-- 文字列の連携を指定回数だけ実行 --//
       	for (int lc = 0; lc <= 50000; lc++) {
            buf = buf.append("add");
            buf = buf.append(Integer.toString(lc));

            //-- 一定の間隔でログ出力 --//
            if (lc % 10000 == 0) {
                Date now = new Date();
                System.out.print(fmt.format(now) + ":" + 
                    Integer.toString(lc) + "回目のループ終了\r\n");
            }
        }

        //-- 文字列長を表示して終了 --//
        System.out.print("文字列長=" + 
            Integer.toString(buf.length()) + "\r\n");
        System.out.print("end\r\n");
    }
}

上記プログラムの実行結果は以下になります。

hoge>java -Xms256M -Xmx256m BufferConnect
start
2019/04/28 21:55:55.539:0回目のループ終了
2019/04/28 21:55:55.620:10000回目のループ終了
2019/04/28 21:55:55.651:20000回目のループ終了
2019/04/28 21:55:55.658:30000回目のループ終了
2019/04/28 21:55:55.664:40000回目のループ終了
文字列長=388890
end

こちらは、1秒もかかっておらず約0.5秒ですね。


結果

性能では以下の結果となりました。


  • 「+」演算子で文字列を連結する=19秒
  • 「StringBuffer」で文字列を連結する=0.5秒

38倍の差です。

あきらかに「StringBuffer」を使用した方が性能がよいということがわかります。

コーディングの手間を考えると+演算子の方が簡単なので、ついつい手を抜いてしまうんですが、ある程度の数の文字列を連結する場合は、StringBuffrerを使用するべきです。


しかし上記は、ループを5万回繰り返すといった極端な検証の結果となります。

単純な文字列結合では、気にする程度の差は出なかったりします。


まとめ

やっぱり、Java研修で教えてもらった内容は間違っていなかったという結論になりました。


まとめ
  • 性能を考えると、確実にStringBufferを使った方がいい。
  • +演算子とStringBufferは、約40倍の差が出る(場合がある)。
  • “ループ内で文字列結合する場合はStringBufferを使う!”と認識しておけば問題なさそう!

という訳で、皆さん、文字列連結する場合は面倒くさがらずにStringBufferを使うようにしましょう!


それではまた!