1. はじめに
覚えておいて、C#の文字列はイミュータブル(immutable)なんだ。つまり、一度作ったら変更できないってこと。文字列を「変更」する操作は、実は新しい文字列をメモリに作ってるだけなんだよ。
たとえば、こんな感じで書くと:
string hello = "こんにちは";
hello += " 世界!"; // 一見helloが変わったように見えるけど、実は新しいオブジェクトができてる!
変数helloは新しいオブジェクトを指すようになるし、古い方は(どこでも使われてなければ)そのうちガベージコレクタが消してくれる。こういう操作をループで何回もやると、メモリが「ゴミ」で溢れてパフォーマンスが急激に落ちることもある。特に何千、何万もの文字列を結合するときは、毎回新しい文字列が作られて全部コピーされて、古いオブジェクトはGC待ち、プログラムが急に重くなったりするんだ。
そこで登場するのが、このレクチャーの主役、StringBuilderだよ。
using System.Text; // ← これ絶対必要!
StringBuilder sb = new StringBuilder("こんにちは");
sb.Append(" 世界!"); // これなら文字列が速く効率的に変わる<
StringBuilderとの出会い
StringBuilderはSystem.Text名前空間のクラス。でっかい文字列を効率よく作ったり組み立てたりするのが仕事で、新しいオブジェクトを毎回作らずに中身を変更できるのがポイント。
イメージしやすく言うと:
普通の文字列(string)はモザイクのピースみたいなもので、全部取り替えるしかない。でもStringBuilderはマグネットボードみたいなもので、ピースを好きなだけ貼ったり剥がしたりできる。新しいボードを毎回作る必要はないんだ。
StringBuilderを使うべきタイミングは?
- 文字列を何度も変更する(特にループ内で)。
- たくさんの小さいパーツからテキストを組み立てるとき。
- 速度やメモリ効率が気になるとき。
逆に使わなくていい場合は?
- 1~2回だけの簡単な文字列操作なら、普通のstringの方がシンプルで読みやすい。
- なぜか毎回の文字列の状態を全部残したい場合。
2. StringBuilderの作成と基本操作
名前空間のインポート
まずは名前空間をインポートするのを忘れずに。
using System.Text;// ← これ絶対必要!
新しい文字列の作り方
StringBuilder sb1 = new StringBuilder(); // 空っぽ
StringBuilder sb2 = new StringBuilder("スタート"); // 初期テキスト付き
StringBuilder sb3 = new StringBuilder(100); // 100文字分のバッファ確保
主なメソッド
var sb = new StringBuilder("はじまり");
sb.Append(" + 新しいテキストを追加"); // 末尾に追加
sb.AppendLine(" (これは改行付き)"); // 改行付きで追加
sb.Insert(0, "スタート: "); // 0番目に挿入
sb.Remove(0, 7); // 0番目から7文字削除
sb.Replace("テキスト", "STRING"); // 全部の"テキスト"を"STRING"に置換
string result = sb.ToString(); // 最終的な文字列を取得
3. StringBuilderの効率性
例:パフォーマンス比較
たとえば10,000個の数字を連続した文字列にしたいとき(クラスメイト全員の番号を出力しろって言われた場合とか):
素朴なやり方(string)
string text = "";
for (int i = 0; i < 10000; i++)
{
text += i + " "; // 毎回新しい文字列が作られる!
}
Console.WriteLine(text.Substring(0, 100) + "..."); // 出力を短くするため
最適なやり方(StringBuilder)
var sb = new StringBuilder();
for (int i = 0; i < 10000; i++)
{
sb.Append(i).Append(' '); // チェーンで書ける!
}
Console.WriteLine(sb.ToString().Substring(0, 100) + "...");
何が違うの?
最初のやり方はめっちゃ遅くなるし、メモリも無駄に食う。2つ目は速いしメモリも節約できるよ。
4. StringBuilderの人気メソッド
Append — 文字列や他の型(自動で文字列に変換される)を追加:
sb.Append("テキスト");
sb.Append(123); // 123は"123"になる
sb.Append('!'); // 文字
AppendLine — 文字列+改行を追加:
sb.AppendLine("ストリング"); // "ストリング\n"を追加
Insert — 指定した位置に文字列を挿入:
sb.Insert(0, "Hello, "); // 先頭に挿入
Remove — いくつかの文字を削除:
sb.Remove(3, 4); // 3番目から4文字削除
Replace — 部分文字列や文字を全部置換:
sb.Replace("old", "new");
sb.Replace('a', 'A');
ToString — 完成した文字列を返す。
文字列の切り捨て
stringと違って、StringBuilderオブジェクトは長さを新しく指定できる。その長さまでで切り捨てられるよ。
sb.Length = 10; // 10文字目以降は消える!
5. 使い方のコツとよくあるミス
ToString()が大事な理由
StringBuilderを使ってる間は、手元にあるのは「文字列」じゃなくて特殊なオブジェクト。多くのメソッド(たとえばConsole.WriteLine)は自動で文字列に変換してくれるけど、たまにハマることもある。たとえば普通の文字列と比較したり、こんな風に書くと:
if (sb == "こんにちは") { ... } // あれ?sbが"こんにちは"でもこれは動かない
結果はfalseになる。だって比較してるのは違う型のオブジェクトだからね。
正しくはこう:
if (sb.ToString() == "こんにちは") { ... }
ミュータブル(変更可能)
stringと違って、StringBuilderはミュータブル(mutable)!1つのオブジェクトで何回でも中身を変えられるから、速さで有利なんだ。
最後にToString()を忘れずに。忘れると、System.Text.StringBuilderみたいな謎のテキストが返ってくるだけだよ。
バッファサイズと再割り当て
StringBuilderの中身は実は文字の配列。もし「めっちゃ長い文字列作るぞ!」って最初からわかってるなら、コンストラクタで予想される長さを渡しておくとちょっと速くなる。
var bigBuilder = new StringBuilder(50000); // 5万文字を想定
でも間違えても大丈夫!StringBuilderは必要なら自動でバッファを拡張してくれる。ただちょっと遅くなるだけ。
メモリオーバー?
理論的には、もし無限に追加しまくって何十億文字とかやるとOutOfMemoryExceptionになるかも。でも普通の用途ならまず起きないから安心して。
GO TO FULL VERSION