web-dev-qa-db-ja.com

StringBuilderを賢く使用する方法は?

最初にStringBuilderクラスを使用することについて少し混乱しています。

stringオブジェクトの連結操作は、常に既存のstringと新しいデータから新しいオブジェクトを作成します。 StringBuilderオブジェクトは、新しいデータの連結に対応するためのバッファーを維持します。空きがある場合、新しいデータがバッファの最後に追加されます。それ以外の場合は、新しい大きなバッファーが割り当てられ、元のバッファーのデータが新しいバッファーにコピーされてから、新しいデータが新しいバッファーに追加されます。

しかし、StringBuilderの新しいインスタンスを作成しないようにするために、Stringインスタンスを作成するポイントはどこにありますか? 「1対1」で取引されているようです。

static void Main(string[] args)
{
    String foo = "123";
    using (StringBuilder sb = new StringBuilder(foo)) // also sb isn't disposable, so there will be error
    {
        sb.Append("456");
        foo = sb.ToString();
    }

    Console.WriteLine(foo);
    Console.ReadKey();
}

なぜ使うべきではないのか

+=

Edit:わかりました、StringBuilder(まだこれはコード標準では正しいです)が、これは1つのstringだけで使用する価値はありませんか?

63

変更不変string sのような構造は、構造をコピーすることで実行する必要があり、それにより、より多くのメモリを消費し、アプリケーションの実行時間(GC時間なども増加します)。

StringBuilderは、同じ可変オブジェクトを操作に使用することでこの問題を解決します。

ただし:

コンパイル時にstringを次のように連結する場合:

string myString = "123";
myString += "234";
myString += "345";

実際にそのようなものにコンパイルされます:

string myString = string.Concat("123", "234", "345");

この関数は、関数に入るStringBuildersの数がわかっているため、stringを使用するよりも高速です。

そのため、コンパイル時に既知のstring連結の場合は、string.Concat()を優先する必要があります。

次の場合のようなstringの不明な数に関しては:

string myString = "123";
if (Console.ReadLine() == "a")
{
    myString += "234";
}
myString += "345";

現在、コンパイラはstring.Concat()関数を使用できませんが、StringBuilderは、6-7以上のstringsで連結が行われた場合にのみ、時間とメモリ消費がより効率的になるようです。

悪い練習用法:

StringBuilder myString = new StringBuilder("123");
myString.Append("234");
myString.Append("345");

ファインプラクティスの使用法(ifが使用されていることに注意してください):

StringBuilder myString = new StringBuilder("123");
if (Console.ReadLine() == "a")
{
    myString.Append("234");
}
myString.Append("345");

ベストプラクティスの使用法(whileループが使用されていることに注意してください):

StringBuilder myString = new StringBuilder("123");
while (Console.ReadLine() == "a")
{
    myString.Append("234"); //Average loop times 4~ or more
}
myString.Append("345");
97
Tamir Vered

string不変クラスです。変更することはできません。新しいstringsを作成するだけです。

したがって、result += a;と記述すると、その時点で3つの個別のstringsがメモリにあります:aresultの古い値、および新しい値。もちろん、限られた数のstringsのみを連結する場合、これはまったく問題ありません。大規模なコレクションを繰り返し処理するforループでこれを行うと、問題になる可能性があります。

StringBuilderクラスは、これらの場合にパフォーマンスを改善します。新しいstringsを作成して連結の結果を保存する代わりに、同じオブジェクトを使用します。したがって、stringBuilder.Append(a);を使用する場合、「resultの古い値」に相当するものはありません。


このメモリ効率にはもちろん価格が伴います。少数のstrings a StringBuilderを連結するだけでは、不変のstringクラスに比べてオーバーヘッドが大きくなるため、速度に関して効率が低下することがよくあります。


心に留めておくべきことの1つは、.ToString()を呼び出すとStringBuilderの新しいコピーが作成されるため、中間文字列が必要なときにstringの効率が低下する可能性があることです。

12
Dirk

その理由は、stringsが不変であるためです。 stringを連結するとき、新しいstringを作成します。そのため、多くのstringsを連結する必要がある場合、多くのobjectsを作成します。各stringが1回使用されるため、これはメモリの点ではあまりコストがかかりません。ただし、GCには追加の作業が必要です。

StringBuilderただし、毎回同じobjectを使用しますが、使いやすさが犠牲になります。

1
Aron