技術開発日記

技術やら日々思ったことを綴ってます。

Java8の文字列結合

Java8から文字列の結合方法がいくつか追加されたので、簡単にまとめました。

StringJoiner

Java8から新しく追加されたクラスjava.util.StringJoiner。
区切り文字、サフィックス、プレフィックスを埋めて文字列を連結できるので
便利ライブラリGuavaのJoiner#onの代わりになる感じでしょうか。

サンプルコード

public static void main(String[] args) {		
  // 単純に文字列の結合
    StringJoiner sj = new StringJoiner("");
    sj.add("a");
    sj.add("b");
    sj.add("c"); 
  // sj.add("a").add("b").add("c"); でもOK
    System.out.println(sj); //abc

  // 区切り文字を追加して文字列の結合
    StringJoiner sj2 = new StringJoiner(":");
    sj2.add("a");
    sj2.add("b");
    sj2.add("c"); 
  // sj2.add("a").add("b").add("c"); でもOK
    System.out.println(sj2); //a:b:c


  // サフィックス、プレフィックスを追加してのに文字列の結合
    StringJoiner sj3 = new StringJoiner(":", "[", "]");
    sj3.add("1");
    sj3.add("2");
    sj3.add("3"); 
  // sj3.add("1").add("2").add("3"); でもOK
    System.out.println(sj3); //[1:2:3]

  // マージ
    StringJoiner sj4 = sj2.merge(sj3);
    System.out.println(sj4); //a:b:c:1:2:3

  // マージ【2】 順番を逆にするとサフィックス、プレフィックスが追加されるので注意
    sj4 = sj3.merge(sj2);
    System.out.println(sj4); //[1:2:3:a:b:c]

  // マージ【3】 サフィックス、プレフィックスが重複して表示されることはない
    sj4 = sj3.merge(sj3);
    System.out.println(sj4); //[1:2:3:1:2:3]

    // 空の場合の設定
    StringJoiner sj5 = new StringJoiner(":");
    sj5.setEmptyValue("It's EMPTY!!");
    System.out.println(sj5); //It's EMPTY!!
}

String#join

StringJoiner同様文字列の連結に区切り文字を追加できる。
ただ、StringJoinerと違ってサフィックス、プレフィックスの指定はできないのが残念。

サンプルコード

public static void main(String[] args) {		
  // 区切り文字を追加して文字列の結合
    String str = String.join(":", "a","b","c");
    System.out.println(str); //a:b:c

  // Listを引数に結合
  List<String> list = Arrays.asList("a", "b", "c");
  String str2 = String.join(":", list);
  System.out.println(str2); //a:b:c
}

Collectors#joining

Stream#collectメソッドの引数に渡すjava.util.stream.Collectors のjoiningメソッド
Collectorインターフェースの具象クラス。
joining以外にもaveragingIntとかgroupingByといった便利なものがたくさん用意されている。
http://www.ne.jp/asahi/hishidama/home/tech/java/collector.html

サンプルコード

public static void main(String[] args) {		
  List<String> list = Arrays.asList("a", "b", "c");

  // 単純に文字列の結合
    String str = list.stream.collect(Collectors.joining());
  System.out.println(str); //abc

  // 区切り文字を追加して文字列の結合
    String str2 = list.stream.collect(Collectors.joining(":"));
  System.out.println(str2); //a:b:c

    // サフィックス、プレフィックスを追加してのに文字列の結合
    String str3 = list.stream.collect(Collectors.joining(":", "[", "]"));
  System.out.println(str3); //[a:b:c]
}

ちなみにCollectorsクラスの中身を見てみると内部で区切り文字がある場合は「StringBuilder」、ない場合で「StringJoiner 」を内部で使用していてなかなか興味深かった。

詳細:GC: Collectors - java.util.stream.Collectors (.java) - GrepCode Class Source

まとめ

既存のStringBuilderとかも加えるとかなりの種類の方法で文字列の結合ができるようになったけど、最終的に何を使えばいいのかは結局その時々で判断するしかないのか。。

たくさん文字の連結をしないといけない場合はパフォーマンスを意識したものを選択しないといけないし、そうでない場合、例えばListをfilterしたり、mappingした延長で文字列に区切り文字を入れたい場合はそのままCollectors#joiningを使うのが自然だと思う。ちょっとした文字列の結合ならプラス演算子で連結で、といった感じで。

ただ、個人的に普段の開発でそこまで大きい数の文字列を連結するってことはあまりないと思うし、文字列の連結をするときって、だいたいログの出力内容とか、どこかのパスの生成とかが多いと思うので、文字列の連結をするとき(区切り文字を埋め込みたいとき)は、この中で一番使いやすいStringJoinerが使われることが多いのかなと思ったりしている。