Java 入門

Java StringBuilderクラス

Javaにおける StringBuilder クラスは、文字列の変更を効率的に処理するために設計されています。作成後に値を変更できないイミュータブル(不変)な String クラスとは異なり、StringBuilder は新しいオブジェクトを都度生成することなく、文字シーケンスを直接変更できます。

これは大量の文字列操作を行う際に非常に有効で、メモリオーバーヘッドを劇的に削減し、パフォーマンスを向上させることができます。StringBuilder はミュータブル(可変)な文字シーケンスを提供し、文字の追加(追加)、挿入、削除、置換のためのメソッドを備えています。StringBuilder をマスターすることは、効率的な Java コードを書くための鍵となります。

1. StringBuilder クラスを理解する

StringBuilder クラスは java.lang パッケージに属しているため、明示的なインポートなしでプログラム内で直接使用できます。本質的には、変更可能な文字のシーケンスを表します。

1.1 String の不変性 vs StringBuilder の可変性

これが両者の最も核心的な違いです:

  • String:オブジェクトはイミュータブルです。String を変更(連結や置換など)するたびに、完全に新しい String オブジェクトが作成されます。
String str = "Hello";
str = str + " World"; // 新しい String オブジェクトが作成される
str = str.replace("World", "Java"); // さらに新しい String オブジェクトが作成される
System.out.println(str); // 出力: Hello Java

上の例では、実際には "Hello"、"Hello World"、"Hello Java" という3つのオブジェクトがメモリ上に作成されています。

  • StringBuilder:オブジェクトはミュータブルです。変更は基底の文字シーケンスに直接作用し、余分なオブジェクトを生成しません。
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // オリジナルの StringBuilder オブジェクトを直接変更
sb.replace(6, 11, "Java"); // 同じオブジェクトをそのまま変更
System.out.println(sb); // 出力: Hello Java

この方式は、ループ内での連結や頻繁な修正が発生するシーンで極めて高い効率を発揮します。

2. StringBuilder オブジェクトの作成

StringBuilder は以下のようないくつかの方法でインスタンス化できます:

1. 空のオブジェクト: デフォルトのキャパシティ(通常は 16 文字)を持つインスタンスを作成します。

StringBuilder sb1 = new StringBuilder();
System.out.println(sb1.capacity()); // 出力: 16

2. 初期キャパシティの指定: 文字列が長くなることがあらかじめわかっている場合、サイズを指定して自動拡張(リサイズ)のオーバーヘッドを減らすことができます。

StringBuilder sb2 = new StringBuilder(50);

3. 初期内容の指定: 既存の文字列に基づいて作成します。キャパシティは通常、文字列の長さ + 16 に設定されます。

StringBuilder sb3 = new StringBuilder("Initial Text");

3. StringBuilder の主要メソッド

StringBuilder はテキスト操作のための豊富なメソッドを提供しています:

  • append(): 末尾に文字列や他のデータ型を追加します。
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World").append(123); // メソッドチェーンをサポート
  • insert(): 指定したインデックス位置に内容を挿入します。
sb.insert(5, ", Java"); // 実行結果: "Hello, Java World"
  • replace(): 指定した範囲内の文字シーケンスを置換します。
sb.replace(6, 11, "Java");
  • delete() および deleteCharAt(): 範囲指定または特定のインデックスの文字を削除します。
sb.delete(5, 11); // インデックス 5 から 11 の内容を削除
sb.deleteCharAt(4); // インデックス 4 の文字を削除
  • reverse(): 文字シーケンス全体を反転させます。
sb.reverse(); // 例: "Hello" -> "olleH"
  • length() と capacity():
    • length() は実際の文字数を返します。
    • capacity() は現在割り当てられているストレージ空間のサイズを返します。

4. StringBuilder vs StringBuffer

Java には StringBuffer という非常によく似たクラスも存在します。機能はほぼ同一ですが、主な違いは以下の点です:

  • StringBuffer: スレッドセーフ(同期化)。マルチスレッド環境に適していますが、同期化のオーバーヘッドがあるため、シングルスレッドでは速度が低下します。
  • StringBuilder: スレッドセーフではありません。シングルスレッド環境でのパフォーマンスがより優れており、ほとんどの一般的なシナリオではこちらが推奨されます。

5. 実践ケーススタディ

5.1 カンマ区切りのリストを構築する

String[] items = {"Apple", "Banana", "Orange"};
StringBuilder sb = new StringBuilder();

for (int i = 0; i < items.length; i++) {
    sb.append(items[i]);
    if (i < items.length - 1) {
        sb.append(", ");
    }
}
System.out.println(sb.toString()); // 出力: Apple, Banana, Orange

5.2 HTML テーブルの行を生成する

String[] rowData = {"Tanaka", "30", "Tokyo"};
StringBuilder tableRow = new StringBuilder();

tableRow.append("<tr>");
for (String data : rowData) {
    tableRow.append("<td>").append(data).append("</td>");
}
tableRow.append("</tr>");

6. ベストプラクティス

  • ループ内では StringBuilder を使用する: forwhile ループの中で String の + 演算子による連結は絶対に避けてください。大量のテンポラリオブジェクトが生成され、GC(ガベージコレクション)の負荷が高まります。
  • キャパシティのプリセット: 最終的な文字列が約 1000 文字になるとわかっている場合、初期化時に new StringBuilder(1000) と指定することで、内部配列の再割り当てを回避できます。
  • メソッドチェーンの活用: 各メソッドが自分自身のインスタンスを返す特性を活かし、コードを簡潔に記述しましょう: sb.append("a").append("b").reverse();