Java | Java 標準ライブラリ:StringBuilder

Java Java
スポンサーリンク

なぜ StringBuilder が必要になるのか

まず前提として、String は「イミュータブル(不変)」です。
一度作られた String の中身は絶対に変わりません。

その結果、こういうコードを書くと

String s = "";
for (int i = 0; i < 10000; i++) {
    s = s + i;
}
Java

毎回 s + i のたびに「新しい String オブジェクト」が生成され、
古い String は捨てられていきます。

ループ回数が多いと、
「文字列をちょっと足したいだけなのに、オブジェクトを大量に作っては捨てる」
というムダな処理が発生します。

この問題を解決するために用意されているのが StringBuilder です。
StringBuilder は「中身を書き換えられる、文字列バッファ」です。


StringBuilder の基本的な使い方

文字列連結の典型例

StringBuilder の代表的な使い方を、String と比較で見てみましょう。

まず、よくない書き方(String での連結):

String result = "";
for (int i = 0; i < 5; i++) {
    result = result + i;
}
System.out.println(result);  // 01234
Java

これを StringBuilder で書き直すと:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
    sb.append(i);
}
String result = sb.toString();
System.out.println(result);  // 01234
Java

ここで重要なのは

StringBuilder に対しては append でどんどん足していく
最後に toString() で「完成した String」に変換する

という流れです。

ループ中では String を何度も作らず、
StringBuilder の中身(バッファ)を書き換えながら、一つの結果を作り上げていきます。


StringBuilder が「速くなる」理由(深掘り)

String は「毎回新しいオブジェクト」、StringBuilder は「中身だけ更新」

String は不変なので、result = result + i; の実体は

前の result の文字列と i を連結した、新しい String を作る
result 変数を、その新しい String に差し替える

という動作です。

例えば 3 回ループしたときのイメージはこうです。

1回目: "" + 0"0"
2回目: "0" + 1"01"
3回目: "01" + 2"012"

それぞれ "0", "01", "012" という別オブジェクトが作られます。

一方で StringBuilder は「自分の中のバッファを書き換える」だけです。

内部に「可変の文字配列」を持っていて、
append されるたびにその配列に追記していきます。

だから、大量連結するときには

String で毎回新規生成 → 無駄が多い
StringBuilder の1つのインスタンスを使い回す → 無駄が少ない

という差になります。


どんなときに StringBuilder を使うべきか

目安:ループでどんどん文字列を足しているなら検討

初心者のうちは、ざっくりこう考えてください。

1〜数回程度の連結
"Hello, " + name のようなちょっとしたもの
+ をそのまま使って OK(読みやすさ優先)

ループの中で何度も連結している
1行ずつログをつなげる
大量のデータから1つの文字列を組み立てる
StringBuilder を使うとよい

例えば、CSV 1 行を構築する例を考えてみます。

悪い例(String 連結):

String line = "";
for (String value : values) {
    if (!line.isEmpty()) {
        line += ",";
    }
    line += value;
}
Java

良い例(StringBuilder):

StringBuilder sb = new StringBuilder();
for (String value : values) {
    if (sb.length() > 0) {
        sb.append(",");
    }
    sb.append(value);
}
String line = sb.toString();
Java

sb.length() で現在の長さをチェックできるので、
「先頭以外の前にカンマを付ける」などの処理も書きやすくなります。


StringBuilder の主なメソッドとよくある使い方

append:なんでも足す

append は、文字列だけでなく、いろいろな型を直接渡せます。

StringBuilder sb = new StringBuilder();
sb.append("合計: ");
sb.append(10);
sb.append(" 円");

String s = sb.toString();  // "合計: 10 円"
Java

int, long, double, boolean, char などもそのまま渡せるので、
String.valueOf(...) をいちいち書かなくて済みます。

insert:途中に差し込む

途中に文字を入れたいときは insert を使います。

StringBuilder sb = new StringBuilder("abcde");
sb.insert(2, "X");  // インデックス2の位置に挿入

System.out.println(sb.toString());  // "abXcde"
Java

deletereplace などもありますが、
初心者のうちは appendtoString を押さえておけば十分です。

length:現在の長さを確認

length() は「現在の文字数」です。

if (sb.length() > 0) {
    sb.append(",");
}
Java

のように、「最初の要素かどうか」の判定などでよく使います。


String と StringBuilder の変換

String → StringBuilder

既に持っている String に続きの文字を効率的に足したいときは、
StringBuilder に変換してから作業します。

String s = "Hello";
StringBuilder sb = new StringBuilder(s);
sb.append(", World!");

String result = sb.toString();
System.out.println(result);  // "Hello, World!"
Java

このように、「一旦 StringBuilder として編集 → 最後に String に戻す」パターンです。

StringBuilder → String

toString() で、完成した中身を String として取り出します。

String result = sb.toString();
Java

ここで返ってきた String は、もちろんイミュータブルな普通の String です。


StringBuffer との違い(軽く触れておく)

StringBuffer はスレッドセーフ版

StringBuilder によく似たクラスとして StringBuffer があります。

StringBuffer は内部のメソッドが同期化されていて、
複数スレッドから同時に触られても安全な設計になっています。

一方 StringBuilder は同期化されていないので、
単一スレッドでの利用を前提にした、より軽量・高速なクラスです。

現代の Java コードでは、

単一スレッド内で文字列を構築 → StringBuilder を使う
どうしてもマルチスレッドで同じバッファを共有する必要がある → StringBuffer も検討

という使い分けが一般的です。

初心者のうちは

「普通は StringBuilder を使う。StringBuffer は歴史的事情で残っているっぽい」

くらいの理解で問題ありません。


まとめ:StringBuilder をいつ、どう使うか

StringBuilder を一言で表すと、

「たくさん文字を足したいときの、効率の良い下書き用バッファ」

です。

意識しておきたいポイントは:

String はイミュータブルなので、連結のたびに新しいオブジェクトができる
ループでの大量連結は StringBuilder に任せると効率が良い
append で何でも足して、最後に toString() で完成品の String を取り出す

タイトルとURLをコピーしました