タイトル: StringBuffer
SEOタイトル: Java StringBuffer 完全ガイド(可変文字列 / append / insert / StringBuilder との違い / スレッドセーフ)
| この記事の要点 |
|
StringBuffer とは
StringBuffer は Java で可変の文字列を表すクラスです。String 型が不変(immutable)であるのに対し、StringBuffer は同じオブジェクトを書き換えられるため、文字列の連結や編集を大量に行う場合にメモリと CPU の両方で効率的です。
String との違い
| 項目 | String | StringBuffer |
|---|---|---|
| 可変性 | 不変(immutable) | 可変(mutable) |
| 連結方法 | + 演算子 | append() メソッド |
| 連結のコスト | 毎回新オブジェクト生成 | 同オブジェクトを更新 |
| 大量連結時の性能 | 遅い(O(N²)) | 速い(O(N)) |
| スレッドセーフ | ○(不変だから) | ○(同期化されている) |
基本的な使い方
StringBuffer sb = new StringBuffer("val1");
sb.append(" val2"); // 末尾に追加
sb.append(" val3");
System.out.println(sb); // val1 val2 val3
// String に変換
String result = sb.toString();
主要メソッド
| メソッド | 用途 |
|---|---|
append(x) | 末尾に追加(あらゆる型を受ける) |
insert(i, x) | 位置 i に挿入 |
delete(start, end) | 範囲削除 |
deleteCharAt(i) | 1 文字削除 |
replace(start, end, str) | 範囲置換 |
reverse() | 逆順 |
length() | 長さ |
charAt(i) | i 番目の文字 |
substring(s, e) | 部分文字列 |
toString() | String に変換 |
使用例
StringBuffer sb = new StringBuffer();
// append: あらゆる型を受け取れる
sb.append("ID=");
sb.append(123); // int
sb.append(", flag=");
sb.append(true); // boolean
sb.append(", pi=");
sb.append(3.14); // double
System.out.println(sb); // ID=123, flag=true, pi=3.14
// insert: 0 文字目に "Hello " を挿入
sb.insert(0, "Hello ");
// delete: 5 〜 8 文字目を削除
sb.delete(5, 8);
// replace: 範囲を置換
sb.replace(0, 5, "Hi");
// reverse: 逆順
StringBuffer r = new StringBuffer("abcdef");
r.reverse(); // fedcba
System.out.println(r);
String との連結性能比較
1 万回ループで文字列を連結すると、性能差が劇的に表れます。
// NG: String の + 連結(毎回新オブジェクト)
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 内部で StringBuilder が作られて消える
}
// ループ全体で O(N²)、Big-O では 10000² = 1 億回の文字コピー
// OK: StringBuffer で連結
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
// O(N)、ループ全体で 1 万回。100倍以上速い
StringBuffer vs StringBuilder
Java 5 で導入された StringBuilder は、StringBuffer とほぼ同じ APIを持つ可変文字列クラスですが、スレッドセーフではない代わりに高速です。
| 項目 | StringBuffer | StringBuilder |
|---|---|---|
| 導入 | Java 1.0 | Java 5 |
| スレッドセーフ | ○(synchronized) | × |
| 速度 | 遅い(同期化のオーバーヘッド) | 速い |
| 推奨度 | マルチスレッド共有時のみ | ★ 通常はこちら |
結論: シングルスレッドや1 メソッド内のローカル変数として使うなら StringBuilder。複数スレッドで共有するなら StringBuffer。実務では StringBuilder を使う場面が圧倒的に多いです。
String への変換
StringBuffer sb = new StringBuffer("hello");
// 推奨: toString()
String s1 = sb.toString();
// 可: new String(sb)
String s2 = new String(sb);
// 部分文字列
String s3 = sb.substring(0, 3); // "hel"
容量と性能
StringBuffer は内部にバッファ(char 配列)を持ち、容量が足りなくなると拡張します。あらかじめサイズが分かっている場合、初期容量を指定すると拡張コストを削減できます。
// デフォルトの初期容量は 16 文字
StringBuffer sb1 = new StringBuffer();
// 容量を指定
StringBuffer sb2 = new StringBuffer(1024);
// 文字列リテラルで初期化(length + 16)
StringBuffer sb3 = new StringBuffer("hello");
// 容量の確認
System.out.println(sb3.capacity()); // 21
System.out.println(sb3.length()); // 5
// 容量の確保
sb3.ensureCapacity(100);
メソッドチェーン
append / insert 等は自分自身を返すので、メソッドチェーンで書けます。
String result = new StringBuffer()
.append("Hello, ")
.append("World")
.append('!')
.append(2026)
.toString();
// "Hello, World!2026"
String.format と比較
固定フォーマットの文字列を組み立てるなら String.format や printf 形式の方が読みやすいことがあります。
// StringBuffer
String s1 = new StringBuffer()
.append("User=").append(name)
.append(", age=").append(age)
.toString();
// String.format
String s2 = String.format("User=%s, age=%d", name, age);
// テキストブロック(Java 15+)+ formatted
String s3 = """
User=%s, age=%d
""".formatted(name, age);
FAQ
Q: + 連結は常に遅い?
A: 単発の連結("A" + "B")は問題なし。コンパイラが StringBuilder に変換するため1 行内の連結は最適化されています。問題なのはループ内の +=。
Q: StringBuffer は古い?
A: 古いコードでは見かけますが、新規コードでマルチスレッド共有が不要なら StringBuilder を使うのが現代の作法です。
関連
- String — 不変な文字列クラス
- StringBuilder — スレッド非対応の高速版
- String.format — printf スタイルのフォーマット
- String.join — リストの結合