小ネタ。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でのシェアや、今後も情報発信しますのでフォローよろしくお願いします。