きり丸の技術日記

技術・エンジニアのイベント・資格等はこちらにまとめる予定です

Javaでカンマ区切りの文字列を作る(StringJoiner, StringBuilder, String)

小ネタ。JavaのStringJoinerを使うことで、カンマ区切りの文字列を簡単に作れます。

今回の記事は、StringBuilderを使ったカンマ区切りの文字列の作り方をメモします。既存のStringBuilderでのカンマ区切りの文字列を作り方も併記します。

環境

  • Java
    • 15

クラスの登場時期

  • StringBuilder
    • 1.5
  • StirngJoiner
    • 1.8

StirngJoinerはJava8から導入されているので、多くのプロジェクトで使えると思います。

ゴール

  • 配列の「りんご」、「ゴリラ」、「ラッパ」を元に「りんご,ゴリラ,ラッパ」の文字列を作成する
  • StringJoinerの使い方が分かる

StringJoinerでの構築方法

StringJoinerの第一引数は区切り文字を指定します。今回は区切り文字にカンマを指定します。

public String joiner(List<String> words) {

  var sj = new StringJoiner(",");
  for (String word : words) {
    sj.add(word);
  }
  return sj.toString();
}

StringBuilderでの構築方法

実装方式は色々ありますが、大きくは2つの方式があります。

1つは配列の文字を取り出すたびに文字とカンマを追加し、処理の最後に余分なカンマを削除する方法です。

public String builder(List<String> words) {
  var sb = new StringBuilder();
  for (String word : words) {
    sb.append(word);
    sb.append(",");
  }
  // カンマを削除
  sb.delete(sb.length() - 1, sb.length());

  return sb.toString();
}

1つは配列の文字を取り出し、最後の配列の文字以外にカンマを追加する方法です。

public String builder2(List<String> words) {
  var sb = new StringBuilder();
  for (int i = 0; i < words.size(); i++) {
    sb.append(words.get(i));
    // 最後の配列の文字以外はカンマを追加する
    if (words.size() - 1 != i) {
      sb.append(",");
    }
  }

  return sb.toString();
}

処理自体は簡単ですが、StringJoinerと比べると一手間を加える必要があります。当然記述が少ない方がエラーが発生しづらいので、StringJoinerを使える環境であればStringJoinerを使いましょう。

その他のStringJoinerの挙動

nullを投入した時の挙動について

StringJoinerにnullを追加した場合、返却値は残念ながら"null"となります。Java8で導入された比較的あたらしいクラスですが、誤って「null様」を表示しないように注意しましょう。

@Test
void test_05() {
  List list = new ArrayList();
  list.add(null);
  assertThat(
      target.tab(list)
  ).isEqualTo("null");
}

カンマ以外の区切り文字について

区切り文字はなんでも定義できます。タブ文字でも、1文字以外でもできます。

public String tab(List<String> words) {
  var sj = new StringJoiner("\t");
  for (String word : words) {
    sj.add(word);
  }
  // りんご ゴリラ   ラッパ
  return sj.toString();
}

public String addAiueo(List<String> words) {
  var sj = new StringJoiner("AIUEO");
  for (String word : words) {
    sj.add(word);
  }
  // りんごAIUEOゴリラAIUEOラッパ
  return sj.toString();
}

結合した文字の前後を囲みたい

StringJoiner(String delimiter)だけでなく、StringJoiner(String delimiter, String prefix, String suffix)というメソッドがあります。

最終的に結合した文字をカッコで囲んだり、ブラケットで囲みたい時にはこちらを使うと便利です。

public String addBracket(List<String> words) {
  var sj = new StringJoiner(",", "{", "}");
  for (String word : words) {
    sj.add(word);
  }
  // {りんご,ゴリラ,ラッパ}
  return sj.toString();
}

もっと簡単に使いたい

Java8からStringにjoin()が追加されています。前後を何かで囲みたい、という要望が無ければ、こちらを使ったほうが簡単には書けます。

String.join(",", param);

内部的にはStringJoinerを使っているので、特に特別なことはしていません。

public static String join(CharSequence delimiter,
        Iterable<? extends CharSequence> elements) {
    Objects.requireNonNull(delimiter);
    Objects.requireNonNull(elements);
    StringJoiner joiner = new StringJoiner(delimiter);
    for (CharSequence cs: elements) {
        joiner.add(cs);
    }
    return joiner.toString();
}

できないこと

追加文字自体の前後に何かを加えたい

文字全体を指定した文字で囲むことはできますが、追加した文字を追加しながら囲むことができません。

例えば次のように、全体をブラケットで囲みつつ、追加した文字をダブルクォーテーションで加えたいとなるとStringJoinerだけでは上手くいきません。MessageFormatを使ったり、プラスで結合する等で工夫してください。

public String addBracketAndDoubleQuote(List<String> words) {
  var sj = new StringJoiner(",", "{", "}");
  for (String word : words) {
    sj.add(MessageFormat.format("\"{0}\"", word));
  }
  // {"りんご","ゴリラ","ラッパ"}
  return sj.toString();
}
  

// MyBatisのSQLを動的に生成するときのやり方で使う
sj = new StringJoiner(",", "(", ")");
for (String word : words) {
  sj.add("#{" + word + "}");
}
// (#{りんご}, #{ゴリラ}, #{ラッパ})

ソースコード

終わりに

元々StringJoinerのことは知っていましたが、やはり便利ですね。

この検証中にStirngのjoinメソッドを知ることができたのは大きな収穫でした。元々は、「配列なら次の書き方でもカンマ区切りの文字列を取得できます。」と紹介しようと思っていたのに、IntelliJ IDEAからのサジェスチョンがあり気付くことができました。

IDEが知らないことを教えてくれるのは成長を感じられて良いですね。

Stream.of("りんご", "ゴリラ", "ラッパ").collect(Collectors.joining(","));

今後も知っていることはブログにしつつ、ローカルで検証することで知識量を増やしていきたいです。


この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。

類似

f:id:nainaistar:20210617225151p:plain