文字列連結は「小さなピースを一つのメッセージにまとめる」技
業務システムでは、ログメッセージ、SQL、URL、エラーメッセージ、メール本文など、
「いくつかの値を組み合わせて一つの文字列にしたい」場面がひたすら出てきます。
ここを何も考えずに + で書き散らすと、パフォーマンスが落ちたり、可読性が下がったり、
「どこで何が連結されているのか分からない」状態になりがちです。
だからこそ、「業務で使う文字列連結の“型”をユーティリティとして決めておく」と、
コード全体がかなり落ち着きます。
なぜ + 連結だけに頼るとつらくなるのか
小さな例では便利、大きくなると一気に読みにくくなる
まず、+ 連結自体は悪ではありません。小さな例ではむしろ読みやすいです。
String message = "Hello, " + name + "!";
Javaこれは全然アリです。
問題になるのは、こういうパターンです。
String sql =
"SELECT * FROM users " +
"WHERE status = '" + status + "' " +
"AND created_at >= '" + from + "' " +
"AND created_at < '" + to + "'";
Java一見読めそうですが、値の部分に ' が入ったら壊れますし、
どこまでが固定文字列で、どこからが変数なのかがパッと見で分かりづらくなります。
さらに、ループの中で + 連結を繰り返すと、String がイミュータブル(変更不可)であるがゆえに、
内部で何度も新しいインスタンスが生成され、パフォーマンスにも悪影響が出ます。
ここで登場するのが、StringBuilder と「連結をユーティリティに閉じ込める」という発想です。
基本の武器:StringBuilder を素直に使う
「何度も足していく」なら、最初から StringBuilder にする
StringBuilder は、「文字列を何度も追加していく」ためのクラスです。
内部で可変のバッファを持っていて、append するたびに中身を書き足していきます。
StringBuilder sb = new StringBuilder();
sb.append("Hello, ");
sb.append(name);
sb.append("!");
String message = sb.toString();
Javaこれを見て、「+ より長くてダルい」と感じるのは自然です。
なので、業務では「パターン化してユーティリティに閉じ込める」のがポイントになります。
例題:ログメッセージを安全に連結するユーティリティ
「キー=値」を並べるパターンを固める
ログでよくあるのが、こんな書き方です。
System.out.println("userId=" + userId + ", action=" + action + ", result=" + result);
Javaこれを毎回手書きするのではなく、「キー=値をカンマ区切りで並べる」ユーティリティを作ります。
public final class LogStrings {
private LogStrings() {}
public static String kv(String key, Object value) {
return key + "=" + String.valueOf(value);
}
public static String joinWithComma(String... parts) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(parts[i]);
}
return sb.toString();
}
}
Java使い方はこうなります。
String log = LogStrings.joinWithComma(
LogStrings.kv("userId", userId),
LogStrings.kv("action", action),
LogStrings.kv("result", result)
);
System.out.println(log);
// userId=u-001, action=LOGIN, result=SUCCESS
Javaここで深掘りしたい重要ポイントは、「“どう連結するか”のルールをユーティリティに閉じ込めている」ことです。
カンマの前後にスペースを入れるか、最後にカンマを付けないか、null をどう表示するか、
そういった細かいルールを一箇所に集約できるので、ログのフォーマットが全体で揃います。
例題:区切り文字で連結する join ユーティリティ
String.join でもいいが、自前で持つと拡張しやすい
Java 8 以降には String.join があります。
String s = String.join(", ", "A", "B", "C"); // "A, B, C"
Javaこれをそのまま使ってもよいのですが、業務では「null をどう扱うか」「空文字を飛ばすか」など、
もう一歩踏み込んだルールを決めたいことが多いです。
そこで、簡単な join ユーティリティを作ります。
public final class Strings2 {
private Strings2() {}
public static String join(String delimiter, Iterable<?> values) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Object v : values) {
if (v == null) {
continue; // null はスキップする方針
}
if (!first) {
sb.append(delimiter);
} else {
first = false;
}
sb.append(v);
}
return sb.toString();
}
}
Java使い方はこうです。
List<String> names = java.util.List.of("山田", null, "佐藤", "鈴木");
String joined = Strings2.join(" / ", names);
System.out.println(joined); // 山田 / 佐藤 / 鈴木
Javaここでのポイントは、「“null を飛ばす”というルールを join 側に持たせている」ことです。
呼び出し側は「null チェックしてから add する」などを意識せずに済みます。
こういう「連結のルール」をユーティリティに閉じ込めると、
画面表示、ログ、CSV など、いろんな場面で同じ感覚で文字列連結ができるようになります。
例題:プレフィックス・サフィックス付きの連結
「[] で囲む」「{} で囲む」をパターン化する
ログやメッセージで、「何かを [] で囲む」「<> で囲む」といった表現もよく出てきます。
String s = "[" + value + "]";
Javaこれも小さいうちはいいのですが、あちこちに散らばると微妙に表記揺れが出ます。
そこで、こんなユーティリティを用意します。
public final class Brackets {
private Brackets() {}
public static String square(Object value) {
return "[" + String.valueOf(value) + "]";
}
public static String angle(Object value) {
return "<" + String.valueOf(value) + ">";
}
public static String curly(Object value) {
return "{" + String.valueOf(value) + "}";
}
}
Java使い方はこうです。
String log = "user " + Brackets.square(userId) + " logged in.";
System.out.println(log);
// user [u-001] logged in.
Javaここでの重要ポイントは、「“見た目のルール”をコード上で名前にしてしまう」ことです。Brackets.square と書いてあれば、「[] で囲む」という意図が一目で分かります。
これを積み重ねていくと、「文字列連結=ただの + の集まり」ではなく、
「意味のあるパーツを組み合わせてメッセージを作る」という感覚に変わっていきます。
パフォーマンスの話を、初心者向けにちゃんと整理する
「ループの中の + は StringBuilder に置き換える」が基本ライン
よく言われるのが、「+ 連結は遅いから全部 StringBuilder にしろ」という極論ですが、
実務的には「ループの中で何度も連結するなら StringBuilder、それ以外は読みやすさ優先で + でもよい」くらいの感覚で十分です。
例えば、こんなコードは要注意です。
String s = "";
for (String part : parts) {
s = s + part; // 毎回新しい String が生成される
}
Javaこれを StringBuilder にするとこうなります。
StringBuilder sb = new StringBuilder();
for (String part : parts) {
sb.append(part);
}
String s = sb.toString();
Javaここで深掘りしたいのは、「“何回連結するか”が重要」ということです。
1〜2回の連結なら + で十分ですが、100回、1000回と増えていくと、StringBuilder を使うかどうかで差が出てきます。
そして、その StringBuilder の使い方も、できるだけユーティリティに閉じ込めてしまえば、
呼び出し側は「ループの中で join を呼ぶ」だけで済むようになります。
まとめ:文字列連結ユーティリティで身につけたい感覚
文字列連結は、「ただ + でつなぐ」だけの話ではなく、
「どんなルールで」「どんな見た目で」「どれくらいの回数を」「どこに責務を持たせて」つなぐか、という設計の話です。
押さえておきたい感覚は、まず「小さな連結は + でよいが、繰り返しや複雑な連結は StringBuilder ベースのユーティリティに寄せる」こと。
次に、「ログ用の kv、区切り文字付き join、括弧で囲むなど、“よく出るパターン”を名前付きのメソッドにしてしまう」こと。
そして、「連結のルール(null の扱い、区切り、フォーマット)をユーティリティに閉じ込めることで、コード全体の見た目と挙動を揃える」ことです。
もしあなたのプロジェクトのどこかに、"userId=" + userId + ", action=" + action + ... のようなコードが散らばっているなら、
その一つを題材にして、ここで作った LogStrings.kv や Strings2.join に置き換えてみてください。
それだけで、「読みやすくて、変更に強くて、パフォーマンスも安定した文字列連結」に、一段レベルアップできます。

