このページでは、JavaのStringBuilderについて「そもそも何者?」「なぜ使うのか」「どう使うのか」という基本から、内部的な動きまでをわかりやすく解説していきます。文字列操作のパフォーマンス向上を目指す際には欠かせないクラスですので、しっかりと理解しておきましょう。
StringBuilderとは
JavaのString
(参考 「String」のいろは)は不変(immutable)と呼ばれ、生成後はその内容を変更できません。例えば以下のようなコードで文字列を結合すると、内部的には新しいString
オブジェクトが毎回生成されます。
String str = "Hello"; str = str + " World";
"Hello"
に対して" World"
を付け足したとき、"Hello World"
という別のString
オブジェクトが新たに作られます。
このような結合処理をループなどで頻繁に行うと、不要なオブジェクトの生成が多発し、パフォーマンスが低下してしまう可能性があるという問題点があります。
StringBuilderの役割
StringBuilder
は、上記の問題点を解決するためのクラスです。
内部的な動作原理
可変バッファ(配列)を使った設計
StringBuilder
は、内部に文字列を保持するための配列(文字配列)を用意しています。例えば初期容量capacity
が16だとすると、最初は16文字分のバッファが確保されているイメージです。
StringBuilder sb = new StringBuilder(); // capacityがデフォルト16
ここに文字をappend
(追記)すると、まだバッファに余裕がある限り、同じ配列の空き領域を使って文字を追加していきます。
バッファが足りなくなったら自動拡張
たとえば現在のバッファが16文字分確保されていて、そこに17文字目を追加しようとするとどうなるでしょうか?
その場合は、内部的により大きな配列を新たに確保し、古い内容をすべてコピーしてバッファを拡張します。拡張頻度を減らすために、単純な+1ではなく、容量を2倍程度に増やす実装となっているため、動的に成長していきます。
StringBuilderとStringBufferの違い
JavaにはStringBuffer
というクラスも存在し、こちらも可変文字列操作のためのクラスです。
基本的にマルチスレッド環境で共有して利用する場合以外は、より高速なStringBuilder
を使うのが一般的と考えてよいでしょう。
よく使われるメソッド
appendメソッド
文字列や数値、オブジェクトなどを連結していくときに最もよく使われるメソッドです。
StringBuilder sb = new StringBuilder(); sb.append("Hello"); sb.append(" World"); sb.append(123); String result = sb.toString(); // "Hello World123"
insertメソッド
特定の位置に文字列や文字、数値などを挿入します。
StringBuilder sb = new StringBuilder("Hello World"); sb.insert(5, ","); // "Hello, World"
deleteメソッド / deleteCharAtメソッド
指定した範囲(または指定した1文字)を削除します。
StringBuilder sb = new StringBuilder("Hello, World"); sb.delete(5, 6); // "Hello World"
replaceメソッド
指定した範囲を別の文字列に置き換えます。
StringBuilder sb = new StringBuilder("Hello World"); sb.replace(5, 11, ", Everyone!"); // "Hello, Everyone!"
reverseメソッド
文字列を逆順にします。
StringBuilder sb = new StringBuilder("abc"); sb.reverse(); // "cba"
capacity / ensureCapacityメソッド
現在のバッファ容量を確認したり、特定の容量を事前に確保したりできます。
StringBuilder sb = new StringBuilder(); System.out.println(sb.capacity()); // デフォルト16 sb.ensureCapacity(100); // 必要に応じてキャパシティを100以上に確保
使用例: ループでの文字列結合
ループ内で文字列をどんどん追加していく場合、StringBuilder
を使わないとオブジェクト生成が頻繁に起きてしまいます。以下の例は悪い例と良い例です。
String
のみで結合
String text = ""; for (int i = 0; i < 100000; i++) { text = text + i; // 毎回新たなStringオブジェクトを生成 }
StringBuilder
で一気に結合
StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100000; i++) { sb.append(i); } String text = sb.toString();

後者ではオブジェクトの再生成が少なく、処理速度が大幅に向上します。

基本編はこちらで終了。ここからは補足的により本質を理解するための説明です。
StringBuilderの本質は「可変長配列(char[]の動的利用)」
ポイント 内部的にはchar[](またはバイト配列)が使われている
StringBuilder
は、Javaの内部実装として可変長配列(動的配列)(参考 配列とは?)を使っています。初期化時に一定サイズ(たとえばデフォルト16)の配列を持ち、そこに文字を格納していきます。Java 9以降の実装ではbyte[]
を使った「compact strings」という最適化が導入されましたが、本質的には「内部で連続したバッファを用いて文字を蓄えている」という点は変わりません。
ポイント “可変”の仕組み: リサイズ(拡張)のタイミング
バッファに収まりきらなくなると、より大きな配列を新たに確保し、既存の内容をコピーしてからバッファ参照を切り替える、というフローで拡張します。
このときの拡張は1文字ずつではなく、大きめに倍増(※Java標準の実装では (oldCapacity + 1) * 2
など)されるため、頻繁に拡張処理が走ることを避けられます。これを「アモータイズされたコスト」と呼び、1回ごとのappend
は平均すると非常に高速になります。
ポイント 文字列の末端を示す「current position」
StringBuilder
には、配列全体の容量(capacity)とは別に、「いま末端がどこか」を示すインデックスが保持されています。
append()
で文字を追記する際は、この末端インデックス位置に文字列を書き込み、末端インデックスを更新。- バッファ容量を超える場合だけ、上記の拡張処理を行う。
こうすることで、何度も文字を追加しても同じバッファ領域上に書き込むだけで済み、String
のようなオブジェクト新規生成は行われません。
実装に基づく多彩な操作が可能になる理由
insert, delete, replace, reverseなど
- insert: 途中に挿入する場合は、内部で要素をシフトし、挿入位置に文字列を書き込みます。
- delete, replace: 指定範囲の要素を削除したり、新たに上書きしたりするだけ。
- reverse: バッファを単純に頭から末端まで走査し、要素を入れ替えるだけ。
このように配列上で要素の移動・書き換えを行うことで、オブジェクトを新たに作らなくても文字列操作が実行可能となります。
toString()で最終的にStringを生成
toString()
を呼び出すと、内部の配列内容から新たにString
オブジェクトを作るステップが入ります。
これにより、「最終的に不変(immutable)な文字列として扱いたい」ときはString
に変換し、それまではStringBuilder
で可変操作を行う、という柔軟な使い分けが可能です。
どんなことが実現できるのか? ~本質理解から見るメリット~
頻繁な連結処理の高速化
最も代表的なのは、「繰り返し文字列連結を行う」ような場面でのパフォーマンス向上です。
- 例: ログメッセージ生成、HTML/JSON/XMLなどテキスト構造の生成、レポートの組み立て
- 新しい
String
を作らずに内部バッファを伸長していくため、大量のオブジェクト生成を回避できます。
途中挿入や削除にも対応
String
は不変なので、途中で文字列を差し込む・削除する場合、通常は新しいString
を生成するコストがかかります。StringBuilder
なら配列上で書き換えできるため、可変長配列の操作に近い感覚で扱えます。
可変でありながら最終的には不変文字列に変換できる
StringBuilder
内部のバッファはあくまで一時的な編集用という位置づけです。
最終成果物が完成したらtoString()
でオブジェクト化し、以降は不変のString
として扱う。この流れで、安全性と効率性の両立が実現しています。
まとめ: “動的配列+不変文字列の住み分け”という設計
Javaの文字列において
String
(immutable) … 文字列を不変オブジェクトとして扱う(安全性、スレッドセーフ、ハッシュ計算の保持などの利点)StringBuilder
(mutable) … 可変長配列を利用し、動的に文字列を構築・編集する(パフォーマンス優位)
という2種類を用意しているのは、用途に応じた最適解を選べるようにするためです。
- 変更の必要がなく、不変であることで安全や利便が得られる場所では
String
。 - 頻繁に変更・連結する必要があれば
StringBuilder
。
こうした住み分けを理解できると、Java言語の文字列設計の意図もより深く把握できます。