Java8 で String クラスに join メソッドが追加されていました

Java8 で String クラスに、static な join メソッドが追加されていました。
使い方はとってもシンプル。

    String message = String.join("-", "Java", "is", "cool");
    // message には "Java-is-cool" が代入されています。

引数に、任意の数の CharSequence *1、もしくは Iterable<? extends CharSequence> 型のオブジェクトを渡すだけ。

実装

処理は、ほどんど java.util.StringJoiner クラス*2が行っています。

    /**
     * {@code CharSequence elements} のコピーを指定の {@code delimiter} で
     * 結合した文字列を返します。
     *
     * <blockquote>For example,
     * <pre>{@code
     *     String message = String.join("-", "Java", "is", "cool");
     *     // message には "Java-is-cool" が代入されています。
     * }</pre></blockquote>
     *
     * もし要素が null なら、{@code "null"}を追加します。
     *
     * @param  delimiter 各要素を区切る区切り文字
     * @param  elements ひとつに結合する要素
     *
     * @return {@code elements}{@code delimiter} で区切られた、
     *         新しい {@code String} 
     *
     * @throws NullPointerException {@code delimiter} または {@code elements}
     *         {@code null} のとき
     *
     * @see java.util.StringJoiner
     * @since 1.8
     */
    public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

StringJoiner は public なクラスなので、独自に使うこともできるようになっています。
こっちを直接使うと、prefix, suffix も指定できるようです。


その StringJoiner 自体はとてもシンプルです。
以下は、今回の話に関連しているところだけ抜き出したものです。遅延初期化していたり、そもそも結合処理自体をひとつのクラスとして実装しているなど、細かく工夫されているなーと感じました。

public final class StringJoiner {
    private final String prefix;
    private final String delimiter;
    private final String suffix;

    private StringBuilder value;

    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        // (省略)
    }
    
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }

    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }
}

まとめ

これでプロジェクトごとに毎回 join メソッドを書かずに済みますね!

*1:CharSequence は、String や StringBuilder, StringBuffer が実装しています。

*2:これも Java8 で追加されたクラスです。