Java

Java 文字列連結ライブラリの速度比較 [Apache/Guava]

この記事では、Javaの文字列連結に使うライブラリの速度比較をしています。

ある文字列のリストから、カンマなどの区切り文字で区切ってひとつの文字列に結合する処理は、色々な場面でよく使われます。

Join するなどと呼ばれていますが、これらを実現する方法はいくつか存在します。

Javaの標準機能Apache commons langGoogle Guavaなどのライブラリを使うことでも実現が出来ます。

これらの中で結局どのライブラリを使うのが一番速いのか、個人的に気になったので調べてみました。

今回比較したのは、

  • String#join
  • StringUtils#join
  • StringJoiner
  • Guava Joiner

の4つになります。

結論

String.join を選んでおけば問題ない(Java8以降)

Javaのバージョンが古い環境では、StringUtils.joinを利用する。

  • String.join と StringUtils.join ではわずかにString.joinの方が速い
  • String.join の内部実装は StringJoinerなので、StringJoinerと同等の性能
  • StringUtils と Guava Joinerは、どちらもStringBuilderを使って実装されている
  • どれもほとんど速度差がないので、使いやすいものを選んでよい
  • 微差ながら順位をつけるとすると、StringJoiner ≒ String.join > Apache StringUtils.join ≒ Guava Joiner

速度比較のサンプルコード

速度計測に使ったプログラムです。

import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;

import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;

public class StringJoinComparison {
    public void joinTest(int count) {
        final List<String> list = Arrays.asList("a", "b", "c", "あいうえお", "4engineer.net", "java", "sample");
        final String delimiter = ", ";
        final StopWatch stopWatch = new StopWatch();

        stopWatch.start();
        for (int i = 0; i < count; i++) {
            stringJoin(list, delimiter);
        }
        stopWatch.stop();
        System.out.println("String.join: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " msec");

        stopWatch.reset();

        stopWatch.start();
        for (int i = 0; i < count; i++) {
            stringUtilsJoin(list, delimiter);
        }
        stopWatch.stop();
        System.out.println("StringUtils.join: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " msec");

        stopWatch.reset();

        stopWatch.start();
        for (int i = 0; i < count; i++) {
            stringJoiner(list, delimiter);
        }
        stopWatch.stop();
        System.out.println("StringJoiner: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " msec");

        stopWatch.reset();

        stopWatch.start();
        for (int i = 0; i < count; i++) {
            guavaJoiner(list, delimiter);
        }
        stopWatch.stop();
        System.out.println("GuavaJoiner: " + stopWatch.getTime(TimeUnit.MILLISECONDS) + " msec");
    }

    private String stringJoin(List<String> list, String delimiter) {
        return String.join(delimiter, list);
    }

    private String stringUtilsJoin(List<String> list, String delimiter) {
        return StringUtils.join(list, delimiter);
    }

    private String stringJoiner(List<String> list, String delimiter) {
        StringJoiner joiner = new StringJoiner(delimiter);
        for (String cs: list) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

    private String guavaJoiner(List<String> list, String delimiter) {
        Joiner joiner = Joiner.on(delimiter);
        return joiner.join(list);
    }
}

速度計測結果

それぞれ、30,000,000回の文字列結合を実施した結果になります。
3千万回実行してもこの程度の差なので、ほとんど速度差は無いと言えます。
好きなものを使いましょう。

String.join: 4246 msec
StringUtils.join: 5274 msec
StringJoiner: 4218 msec
GuavaJoiner: 5622 msec

まとめ

最後に、この記事の内容をまとめます。

要点

  • 速度差はどれもそれほど無い
  • ライブラリ不要で使える String.join を使うのが無難(Java8以降)
  • Javaのバージョンが古い環境では、StringUtilsやGuavaのJoinerを利用する

どの実装方法を選んでも速度的には問題ないです。好みや機能で選んでしまって大丈夫だと思います。

Java8以降の環境であれば、依存関係なしで使える String#join が使いやすいと思います。

参考になりましたら幸いです。

この記事は役に立ちましたか?

  • この記事を書いた人
アバター画像

ゴイチ

ソフトウェアエンジニア歴20年。 C/C++, C#, Java, Kotlinが得意で、組込系・スマホ・大規模なWebサービスなど幅広いプログラミング経験があります。 現在は某SNSの会社でWebエンジニアをしています。

-Java
-,