回答:
はい、パフォーマンスの違いは重要です。KB記事「Visual C#で文字列連結のパフォーマンスを向上させる方法」を参照してください。
私は常に明確にするために最初にコーディングを試み、その後パフォーマンスを最適化するように後で試みました。これは、逆に行うよりもはるかに簡単です。ただし、2つのアプリケーションのパフォーマンスに大きな違いがあることを知ったので、今はもう少し慎重に考えています。
幸い、コードでパフォーマンス分析を実行して、どこに時間を費やしているかを確認しStringBuilder
、必要な場所で使用するように変更するのは比較的簡単です。
次のようなものがある場合、ジリアンが4文字列について言ったことを明確にするため:
string a,b,c,d;
a = b + c + d;
次に、文字列とプラス演算子を使用すると、より高速になります。これは(Ericが指摘するように、Javaのように)内部でStringBuilderを自動的に使用するためです(実際には、StringBuilderも使用するプリミティブを使用します)。
ただし、あなたがしていることがより近い場合:
string a,b,c,d;
a = a + b;
a = a + c;
a = a + d;
次に、StringBuilderを明示的に使用する必要があります。.Netは無意味なため、ここでは自動的にStringBuilderを作成しません。各行の終わりの "a"は(不変の)文字列でなければならないため、各行にStringBuilderを作成して破棄する必要があります。速度を上げるには、構築が完了するまで同じStringBuilderを使用する必要があります。
string a,b,c,d;
StringBuilder e = new StringBuilder();
e.Append(b);
e.Append(c);
e.Append(d);
a = e.ToString();
a
がローカル変数であり、それが参照するオブジェクトが他の変数(別のスレッドからアクセス可能である可能性がある)に割り当てられていない場合、優れたオプティマイザは、このシーケンス中に他のコードがa
アクセスしていないと判断できます線; 問題の最終的な値のみa
。そのため、これらの3行のコードを、作成されたかのように扱うことができa = b + c + d;
ます。
StringBuilderのが好ましいのIFあなたはPUREパフォーマンスのために、しかし...あなたのコードのパスで複数のループ、またはフォークをしているあなたが逃げることができれば、SINGLEはるかにパフォーマンスで、文字列の宣言、。
例えば:
string myString = "Some stuff" + var1 + " more stuff"
+ var2 + " other stuff" .... etc... etc...;
よりもパフォーマンスが高い
StringBuilder sb = new StringBuilder();
sb.Append("Some Stuff");
sb.Append(var1);
sb.Append(" more stuff");
sb.Append(var2);
sb.Append("other stuff");
// etc.. etc.. etc..
この場合、StringBuildはより保守しやすいと見なすことができますが、単一の文字列宣言よりもパフォーマンスは高くありません。
10回のうち9回...文字列ビルダーを使用します。
余談ですが、string + varは、(一般的に)StringBuilderを内部で使用するstring.Formatアプローチよりもパフォーマンスが優れています(疑わしい場合は...リフレクターを確認してください!)
String
連結vs を使用する場合の速度の違いを示す簡単な例StringBuilder
:
System.Diagnostics.Stopwatch time = new Stopwatch();
string test = string.Empty;
time.Start();
for (int i = 0; i < 100000; i++)
{
test += i;
}
time.Stop();
System.Console.WriteLine("Using String concatenation: " + time.ElapsedMilliseconds + " milliseconds");
結果:
文字列連結の使用:15423ミリ秒
StringBuilder test1 = new StringBuilder();
time.Reset();
time.Start();
for (int i = 0; i < 100000; i++)
{
test1.Append(i);
}
time.Stop();
System.Console.WriteLine("Using StringBuilder: " + time.ElapsedMilliseconds + " milliseconds");
結果:
StringBuilderの使用:10ミリ秒
その結果、最初の反復には15423ミリ秒かかり、2回目の反復でStringBuilder
は10ミリ秒かかりました。
使用のStringBuilder
方がはるかに高速であるように見えます。
このベンチマークは、3つ以下の文字列を組み合わせると、通常の連結が高速になることを示しています。
http://www.chinhdo.com/20070224/stringbuilder-is-not-always-faster/
StringBuilderは、特に500文字列を一緒に追加する場合に、メモリ使用量を大幅に改善できます。
次の例を検討してください。
string buffer = "The numbers are: ";
for( int i = 0; i < 5; i++)
{
buffer += i.ToString();
}
return buffer;
メモリで何が起こりますか?次の文字列が作成されます。
1 - "The numbers are: "
2 - "0"
3 - "The numbers are: 0"
4 - "1"
5 - "The numbers are: 01"
6 - "2"
7 - "The numbers are: 012"
8 - "3"
9 - "The numbers are: 0123"
10 - "4"
11 - "The numbers are: 01234"
12 - "5"
13 - "The numbers are: 012345"
これら5つの数値を文字列の末尾に追加することで、13の文字列オブジェクトを作成しました。そしてそのうちの12は役に立たなかった!うわー!
StringBuilderはこの問題を修正します。よく耳にする「可変文字列」ではありません(.NETのすべての文字列は不変です)。これは、内部バッファ、charの配列を保持することで機能します。Append()またはAppendLine()を呼び出すと、文字配列がchar配列の最後の空のスペースに追加されます。配列が小さすぎる場合は、新しい大きな配列を作成し、そこにバッファーをコピーします。したがって、上記の例では、StringBuilderは、バッファのサイズに応じて、文字列への5つの追加すべてを含む単一の配列のみを必要とする場合があります。StringBuilderに、そのバッファーをコンストラクター内でどれだけ大きくするかを指示できます。
i.ToString()
です。したがって、StringBuilderでは、6 + 1の文字列を作成する必要があります。13個の文字列の作成を7個の文字列の作成に減らしました。しかし、それはまだそれを見るには間違った方法です。6つの数値文字列の作成は関係ありません。結論:あなたは単にによって作成された6つの文字列に言及すべきではありませんでしたi.ToString()
。それらは効率比較の一部ではありません。
はい、StringBuilder
文字列に対して繰り返し操作を実行する際のパフォーマンスが向上します。これは、すべての変更が単一のインスタンスに対して行われるため、などの新しいインスタンスを作成する代わりに時間を大幅に節約できるためString
です。
String
System
名前空間の下StringBuilder
(可変文字列)
System.Text
名前空間の下dotnet mobの記事を強くお勧めします:StringとC#のStringBuilder。
関連スタックオーバーフローの質問:文字列がC#で変更されない場合の文字列の可変性?。
文字列対文字列ビルダー:
最初に知っておくべきことは、これらの2つのクラスがどのアセンブリに存在するかということです。
そう、
文字列はSystem
名前空間に存在します。
そして
StringBuilderはSystem.Text
名前空間に存在します。
以下のための文字列の宣言:
System
名前空間を含める必要があります。このようなもの。
Using System;
そして
以下のためのStringBuilderの宣言:
System.text
名前空間を含める必要があります。このようなもの。
Using System.text;
今実際の質問に来てください。
stringとStringBuilderの違いは何ですか?
これら2つの主な違いは次のとおりです。
文字列は不変です。
そして
StringBuilderは変更可能です。
では、不変と可変の違いについて説明しましょう
可変::変更可能を意味します。
不変::変更不可を意味します。
例えば:
using System;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// String Example
string name = "Rehan";
name = name + "Shah";
name = name + "RS";
name = name + "---";
name = name + "I love to write programs.";
// Now when I run this program this output will be look like this.
// output : "Rehan Shah RS --- I love to write programs."
}
}
}
したがって、この場合は、同じオブジェクトを5回変更します。
だから明白な質問はそれです!同じストリングを5回変更すると、実際には内部で何が起こりますか。
これは、同じ文字列を5回変更するとどうなるかです。
図を見てみましょう。
説明:
最初にこの変数 "name"を "Rehan"に初期化すると、この変数string name = "Rehan"
はスタック "name"に作成され、その "Rehan"値を指します。この行が実行された後: "name = name +" Shah "。参照変数はそのオブジェクトを指していません。
したがってstring
、不変の意味は、一度メモリ内にオブジェクトを作成すると、それらを変更することができないということです。
したがって、name
変数を連結すると、以前のオブジェクトがメモリに残り、別の新しい文字列オブジェクトが作成されます...
上の図から、5つのオブジェクトがあり、4つのオブジェクトは破棄され、まったく使用されません。それらは記憶に残り、記憶の量を占有します。「ガベージコレクター」は、メモリからそのリソースをきれいにする責任があります。
したがって、文字列の場合、文字列を何度も何度も操作すると、作成されたオブジェクトがメモリ内に残ります。
これが文字列変数の話です。
次に、StringBuilderオブジェクトを見てみましょう。 例えば:
using System;
using System.Text;
namespace StringVsStrigBuilder
{
class Program
{
static void Main(string[] args)
{
// StringBuilder Example
StringBuilder name = new StringBuilder();
name.Append("Rehan");
name.Append("Shah");
name.Append("RS");
name.Append("---");
name.Append("I love to write programs.");
// Now when I run this program this output will be look like this.
// output : "Rehan Shah Rs --- I love to write programs."
}
}
}
したがって、この場合は、同じオブジェクトを5回変更します。
だから明白な質問はそれです!同じStringBuilderを5回変更すると、実際には内部で何が起こりますか。
これは、同じStringBuilderを5回変更するとどうなるかです。
説明: StringBuilderオブジェクトの場合。新しいオブジェクトは取得できません。同じオブジェクトはメモリ内で変更されるため、オブジェクトを変更した場合でも、10,000回と言っても、stringBuilderオブジェクトは1つしかありません。
ガベージオブジェクトやnon_referenced stringBuilderオブジェクトが多くないのは、なぜ変更される可能性があるかです。それは時間の経過とともに変化するという意味で変更可能ですか?
違い:
StringBuilderは、使用される追加のメモリを犠牲にして、割り当てと割り当ての数を減らします。適切に使用すると、結果が見つかるまで、コンパイラーがますます大きな文字列を何度も割り当てる必要を完全に取り除くことができます。
string result = "";
for(int i = 0; i != N; ++i)
{
result = result + i.ToString(); // allocates a new string, then assigns it to result, which gets repeated N times
}
対
String result;
StringBuilder sb = new StringBuilder(10000); // create a buffer of 10k
for(int i = 0; i != N; ++i)
{
sb.Append(i.ToString()); // fill the buffer, resizing if it overflows the buffer
}
result = sb.ToString(); // assigns once
StringまたはStringBuilderオブジェクトの連結操作のパフォーマンスは、メモリ割り当てが発生する頻度に依存します。String連結操作は常にメモリを割り当てますが、StringBuilder連結操作は、StringBuilderオブジェクトバッファが小さすぎて新しいデータを収容できない場合にのみメモリを割り当てます。したがって、固定数のStringオブジェクトを連結する場合、連結操作にはStringクラスが適しています。その場合、個々の連結操作は、コンパイラーによって1つの操作に結合されることさえあります。StringBuilderオブジェクトは、任意の数の文字列が連結される場合の連結操作に適しています。たとえば、ループがランダムな数のユーザー入力の文字列を連結する場合などです。
出典:MSDN
StringBuilder
多くの非定数値から文字列を作成するのに適しています。
HTMLまたはXMLドキュメントの複数行の値やその他のテキストのチャンクなど、多くの定数値から文字列を作成している場合、ほとんどすべてのコンパイラーが同じ文字列に追加するだけで済むようになります。 「一定の折りたたみ」、一定の操作がたくさんあるときに解析ツリーを削減するプロセス(これは、のようなものを書くときにも使用されますint minutesPerYear = 24 * 365 * 60
)。また、非定数値が互いに追加されている単純なケースの場合、.NETコンパイラーは、コードを実行するのと同じようなものに削減しますStringBuilder
。
ただし、コンパイラーで追加をより簡単なものに削減できない場合は、が必要になりますStringBuilder
。fizchが指摘するように、それはループ内で発生する可能性が高くなります。
文字列ストレージに使用する前のEnsureCapacity(int capacity)
インスタンスでメソッド呼び出しを使用することで、パフォーマンスが大幅に向上しましたStringBuilder
。私は通常、インスタンス化後のコード行でそれを呼び出します。次のStringBuilder
ようにインスタンス化する場合と同じ効果があります。
var sb = new StringBuilder(int capacity);
この呼び出しにより、必要なメモリが事前に割り当てられます。これにより、複数のAppend()
操作中のメモリ割り当てが少なくなります。どのくらいのメモリが必要になるかを知識に基づいて推測する必要がありますが、ほとんどのアプリケーションでは、これはそれほど難しくありません。私は通常、メモリが少なすぎる側でエラーを起こします(1k程度です)。
EnsureCapacity
直後に呼び出す必要はありませんStringBuilder
。次のStringBuilder
ようにインスタンス化するだけですvar sb = new StringBuilder(int capacity)
。
StringとStringBuilderはどちらも実際には不変です。StringBuilderには、そのサイズをより効率的に管理できるようにするバッファが組み込まれています。StringBuilderのサイズを変更する必要があるのは、ヒープに再割り当てするときです。デフォルトのサイズは16文字ですが、これはコンストラクターで設定できます。
例えば。
StringBuilder sb = new StringBuilder(50);
私のアプローチは常に、4つ以上の文字列を連結する場合はStringBuilderを使用することでした。または、連結を行う方法がわからない場合。
StringBuilder
は非常に効率的ですが、大量の文字列変更を行わない限り、そのパフォーマンスはわかりません。
以下は、パフォーマンスの例を示すコードの短いチャンクです。あなたが見ることができるように、あなたは実際に大きな反復に入るときだけ大きなパフォーマンスの向上を見るようになります。
ご覧のとおり、200,000回の反復には22秒かかりましたが、これを使用した100万回の反復StringBuilder
はほぼ瞬時でした。
string s = string.Empty;
StringBuilder sb = new StringBuilder();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 50000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning String + at " + DateTime.Now.ToString());
for (int i = 0; i <= 200000; i++)
{
s = s + 'A';
}
Console.WriteLine("Finished String + at " + DateTime.Now.ToString());
Console.WriteLine();
Console.WriteLine("Beginning Sb append at " + DateTime.Now.ToString());
for (int i = 0; i <= 1000000; i++)
{
sb.Append("A");
}
Console.WriteLine("Finished Sb append at " + DateTime.Now.ToString());
Console.ReadLine();
上記のコードの結果:
文字列+ 2013年1月28日16:55:40に開始。
2013年1月28日16:55:40に終了した文字列+。
文字列+ 2013年1月28日16:55:40に開始。
文字列+ 2013年1月28日16:56:02に終了。
2013年1月28日16:56:02にSbの追加を開始します。
2013年1月28日16:56:02にSb追加を終了しました。