「高速StringBuilder」は“たくさんつなぐ”場面を丸ごと任せる道具
前回の「文字列連結」で触れたとおり、+ 連結は少ない回数なら問題ありませんが、
ループの中で何百回・何千回と連結するときは、StringBuilder を使うほうが圧倒的に有利です。
ここで一歩進めて、「業務でよくある“たくさんつなぐ処理”を、毎回きれいに・速く書けるようにするためのラッパー」として
“高速StringBuilderユーティリティ”を用意しておく、という発想を持ってほしいんです。
「速いこと」だけが目的ではなく、「書きやすさ」「バグりにくさ」「ルールの一元化」も含めて、
“高速に文字列を組み立てるための型”を作るイメージです。
なぜ「ただの StringBuilder」では足りなくなるのか
速度だけでなく「書き味」と「ルール」が欲しくなる
素の StringBuilder はこう使います。
StringBuilder sb = new StringBuilder();
sb.append("userId=");
sb.append(userId);
sb.append(", action=");
sb.append(action);
String log = sb.toString();
Javaこれでも十分速いのですが、業務コードに大量に出てくると、次のようなモヤモヤが出てきます。
どこでも new StringBuilder() していて、初期容量がバラバラ。
区切り文字(, や \n)の付け方が人によって違う。null の扱いがバラバラ(”null” と出したり、空文字にしたり)。
つまり、「パフォーマンスは良いけど、書き方とルールが揃わない」状態になりがちです。
ここを“ユーティリティ化した StringBuilder”で整えていきます。
基本形:業務用の「FastStringBuilder」を定義する
append のラップと、toString のシンプル化
まずは、StringBuilder をそのまま包んだシンプルなクラスから始めます。
public final class FastStringBuilder {
private final StringBuilder sb;
public FastStringBuilder() {
this.sb = new StringBuilder();
}
public FastStringBuilder(int capacity) {
this.sb = new StringBuilder(capacity);
}
public FastStringBuilder append(Object value) {
sb.append(String.valueOf(value));
return this;
}
@Override
public String toString() {
return sb.toString();
}
}
Java使い方はこうです。
FastStringBuilder fsb = new FastStringBuilder();
String log = fsb
.append("userId=").append(userId)
.append(", action=").append(action)
.append(", result=").append(result)
.toString();
System.out.println(log);
Javaここでの重要ポイントは、「append が Object を受け取り、メソッドチェーンできる」ことです。String.valueOf を中に閉じ込めているので、null を渡しても "null" という文字列になりますし、
呼び出し側は append をつなげていくだけで、+ 連結よりも見通しの良いコードになります。
まだこれだけだと「ただのラッパー」ですが、ここに“業務でよく使うパターン”を足していくと、一気に便利になります。
区切り文字付きの append を組み込む
「最初だけ区切りを付けない」を自動化する
よくあるのが、「カンマ区切りで値を並べたいけど、先頭にはカンマを付けたくない」というパターンです。
これを毎回 if で書くのはダルいので、FastStringBuilder に覚えさせます。
public final class FastStringBuilder {
private final StringBuilder sb;
private boolean first = true;
public FastStringBuilder() {
this.sb = new StringBuilder();
}
public FastStringBuilder(int capacity) {
this.sb = new StringBuilder(capacity);
}
public FastStringBuilder append(Object value) {
sb.append(String.valueOf(value));
return this;
}
public FastStringBuilder appendWithDelimiter(String delimiter, Object value) {
if (!first) {
sb.append(delimiter);
} else {
first = false;
}
sb.append(String.valueOf(value));
return this;
}
@Override
public String toString() {
return sb.toString();
}
}
Java使い方はこうです。
FastStringBuilder fsb = new FastStringBuilder();
String csvLine = fsb
.appendWithDelimiter(",", "山田太郎")
.appendWithDelimiter(",", 30)
.appendWithDelimiter(",", "東京")
.toString();
System.out.println(csvLine); // 山田太郎,30,東京
Javaここで深掘りしたいのは、「“最初だけ区切りを付けない”というよくあるロジックを、ビルダー側に閉じ込めている」ことです。
呼び出し側は「区切り文字」と「値」だけを渡せばよく、if (i > 0) { sb.append(","); } のようなコードを二度と書かなくて済みます。
このパターンは CSV、ログ、SQL の IN 句、メッセージの列挙など、業務のあらゆる場面で使い回せます。
例題:ログメッセージを高速かつ読みやすく組み立てる
「キー=値」を高速に並べる
先ほどの LogStrings.kv と組み合わせると、ログ用の高速ビルダーが作れます。
public final class LogBuilder {
private final FastStringBuilder fsb = new FastStringBuilder();
public LogBuilder kv(String key, Object value) {
fsb.appendWithDelimiter(", ", key + "=" + String.valueOf(value));
return this;
}
public String build() {
return fsb.toString();
}
}
Java使い方はこうです。
LogBuilder lb = new LogBuilder();
String log = lb
.kv("userId", userId)
.kv("action", action)
.kv("result", result)
.build();
System.out.println(log);
// userId=u-001, action=LOGIN, result=SUCCESS
Javaここでのポイントは、「“キー=値をカンマ区切りで並べる”というログの型を、クラスとして表現している」ことです。
内部では FastStringBuilder が効率よく連結してくれているので、
ループで大量にログを組み立てるような場面でも、パフォーマンスと可読性を両立できます。
初期容量を意識して「本当に速くする」
だいたいの長さが分かるなら、最初に教えてあげる
StringBuilder/FastStringBuilder の速度をもう一段上げたいときに効くのが、「初期容量の指定」です。
FastStringBuilder fsb = new FastStringBuilder(256);
Javaこうしておくと、内部バッファが最初から 256 文字分確保されるので、
それを超えない限り、バッファの再確保(拡張)が発生しません。
例えば、「1行あたりだいたい 100〜200 文字くらいのログを 1万行作る」と分かっているなら、FastStringBuilder(256) のように少し余裕を持った容量を指定しておくと、
GC の負荷やメモリアロケーションの回数を減らせます。
ここで深掘りしたいのは、「“速くする”とは、アルゴリズムだけでなく“無駄な再確保を減らす”ことでもある」という感覚です。
毎回 new StringBuilder() しているだけではなく、「この用途ならこのくらいの容量」といった“経験値”をコードに刻んでいくと、
業務システムとしての安定感が一段上がります。
まとめ:高速StringBuilderユーティリティで身につけたい感覚
「高速StringBuilder」は、単に StringBuilder をラップしたクラスではなく、
「たくさん文字列をつなぐ処理を、速く・きれいに・同じルールで書くための“型”」だと捉えてほしいです。
押さえておきたい感覚は、まず「ループや大量連結では、+ ではなくビルダーを使う」こと。
次に、「区切り文字付き append や、キー=値の並びなど、“よく出る連結パターン”をビルダーに覚えさせる」こと。
そして、「用途ごとに初期容量やルールを決めたビルダーを用意し、プロジェクト全体で同じ“書き味”と“速度”を共有する」ことです。
もしあなたのコードのどこかに、s = s + part; のようなループ内連結や、if (i > 0) sb.append(","); が何度も出てくる箇所があれば、
そこを題材にして、ここで作ったような FastStringBuilder ベースの書き方に置き換えてみてください。
それだけで、「速くて、読みやすくて、バグりにくい文字列組み立て」に、一段レベルアップできます。
