StringBuilderの利点を理解しています。
しかし、2つの文字列を連結したい場合は、StringBuilderなしで行う方が良い(速い)と思います。これは正しいです?
どの時点(文字列の数)でStringBuilderを使用する方が良いですか?
StringBuilderの利点を理解しています。
しかし、2つの文字列を連結したい場合は、StringBuilderなしで行う方が良い(速い)と思います。これは正しいです?
どの時点(文字列の数)でStringBuilderを使用する方が良いですか?
回答:
ジェフ・アトウッドによる「マイクロ最適化シアターの悲しい悲劇」を読むことを強くお勧めします。
Simple Concatenation vs. StringBuildervs。他のメソッドを扱います。
さて、あなたがいくつかの数字とグラフを見たいならば、リンクをたどってください;)
しかし、2つの文字列を連結したい場合は、StringBuilderなしで行う方が良い(速い)と思います。これは正しいです?
それは確かに正しいです、あなたは正確に正確に説明されている理由を見つけることができます:
http://www.yoda.arachsys.com/csharp/stringbuilder.html
要約:文字列を一度に連結できる場合は、次のようになります
var result = a + " " + b + " " + c + ..
コピーが作成される場合のみ、StringBuilderを使用しない方がよいでしょう(結果の文字列の長さは事前に計算されます)。
のような構造の場合
var result = a;
result += " ";
result += b;
result += " ";
result += c;
..
毎回新しいオブジェクトが作成されるため、StringBuilderを検討する必要があります。
最後に、記事はこれらの親指のルールを要約しています:
経験則
では、いつStringBuilderを使用する必要があり、いつ文字列連結演算子を使用する必要がありますか?
重要なループで連結する場合は、StringBuilderを必ず使用してください。特に、ループ全体で何回反復するかが(コンパイル時に)確実にわからない場合はそうです。たとえば、一度に1文字ずつファイルを読み取り、+ =演算子を使用して文字列を作成すると、パフォーマンスが低下する可能性があります。
1つのステートメントで連結する必要があるすべてのものを(読みやすく)指定できる場合は、必ず連結演算子を使用してください。(連結するものが多数ある場合は、String.Concatを明示的に呼び出すことを検討してください。区切り文字が必要な場合はString.Joinを呼び出してください。)
リテラルをいくつかの連結ビットに分割することを恐れないでください-結果は同じになります。たとえば、パフォーマンスを損なうことなく、長いリテラルを複数の行に分割することで、読みやすさを向上させることができます。
連結の次の反復をフィードする以外の目的で連結の中間結果が必要な場合、StringBuilderは役に立ちません。たとえば、姓と名からフルネームを作成し、最後に3番目の情報(ニックネームなど)を追加した場合、StringBuilderを使用するとメリットが得られるのはそうでない場合のみです。他の目的のために(名+姓)文字列が必要です(Personオブジェクトを作成する例のように)。
実行する連結がいくつかあり、それらを別々のステートメントで実行したい場合は、どちらに進むかは重要ではありません。どちらの方法がより効率的かは、関係する文字列のサイズの連結の数と、それらが連結される順序によって異なります。コードの一部がパフォーマンスのボトルネックであると本当に確信している場合は、両方の方法でプロファイルまたはベンチマークを行います。
System.Stringは不変のオブジェクトです。つまり、コンテンツを変更するたびに新しい文字列が割り当てられ、これには時間(およびメモリ?)がかかります。StringBuilderを使用すると、新しいコンテンツを割り当てずに、オブジェクトの実際のコンテンツを変更できます。
したがって、文字列に多くの変更を加える必要がある場合は、StringBuilderを使用してください。
実際にはそうではありません...大きな文字列を連結する場合、またはループのように多くの連結がある場合は、StringBuilderを使用する必要があります。
StringBuilder
ループまたは連結が仕様のパフォーマンスの問題である場合にのみ使用する必要があります。
string s = "abcd"
、少なくともそれは最後のことです聞いたことがありますが、変数を使用すると、おそらくConcatになります。
a + "hello" + "somethingelse"
を使用していて、心配する必要はありませんでした。問題が発生する場合は、StringBuilderを使用します。しかし、そもそも気にせず、書く時間も少なくて済みました。
ポイントを証明する簡単なテストアプリは次のとおりです。
class Program
{
static void Main(string[] args)
{
const int testLength = 30000;
var StartTime = DateTime.Now;
//TEST 1 - String
StartTime = DateTime.Now;
String tString = "test string";
for (int i = 0; i < testLength; i++)
{
tString += i.ToString();
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 2000 ms
//TEST 2 - StringBuilder
StartTime = DateTime.Now;
StringBuilder tSB = new StringBuilder("test string");
for (int i = 0; i < testLength; i++)
{
tSB.Append(i.ToString());
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 4 ms
Console.ReadLine();
}
}
結果:
30'000回の反復
1000回の反復
500回の反復
言い換えると
それならあなたは3つまで数えなければならない。三はあなたが数えなければならない数であり、数えている数は三でなければならない。4つは数えません。2つも数えません。ただし、3つに進む場合を除きます。3番目の数字である3番目の数字に到達したら、アンティオキアの聖なる手榴弾をロブベストします。
私は通常、3つ以上の文字列を連結するコードのブロックに文字列ビルダーを使用します。
決定的な答えはなく、経験則だけがあります。私自身の個人的なルールは次のようになります。
StringBuilder
。を使用してください。StringBuilder
。を使用してください。StringBuilder
ます。ただし、2つの文字列を連結する場合は、StringBuilderを使用しない方が適切で高速であると思います。これは正しいです?
はい。しかし、もっと重要なことは、そのような状況でバニラを使用する方がはるかに読みやすいことString
です。一方、ループで使用することは理にかなっており、連結と同じくらい読みやすくなります。
特定の数の連結をしきい値として引用する経験則には注意が必要です。ループ(およびループのみ)で使用することは、おそらく同じように便利で、覚えやすく、より理にかなっています。
意見に影響されない、またはプライドの戦いが続くことのない説明を見つけるのは難しいので、私はこれを自分でテストするためにLINQpadに少しコードを書くことを考えました。
i.ToString()を使用するのではなく、小さいサイズの文字列を使用すると、応答時間が変わることがわかりました(小さなループで表示されます)。
このテストでは、さまざまな反復シーケンスを使用して、時間の測定値を適切に比較可能な範囲に保ちます。
自分で試すことができるように、最後にコードをコピーします(results.Charts ... Dump()はLINQPadの外部では機能しません)。
出力(X軸:テストされた反復回数、Y軸:ティック単位の時間):
反復シーケンス:10、20、30、40、50、60、70、80
コード(LINQPad 5を使用して記述):
void Main()
{
Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
Test(10, 20, 30, 40, 50, 60, 70, 80);
Test(100, 200, 300, 400, 500);
}
void Test(params int[] iterationsCounts)
{
$"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
int testStringLength = 10;
RandomStringGenerator.Setup(testStringLength);
var sw = new System.Diagnostics.Stopwatch();
var results = new Dictionary<int, TimeSpan[]>();
// This call before starting to measure time removes initial overhead from first measurement
RandomStringGenerator.GetRandomString();
foreach (var iterationsCount in iterationsCounts)
{
TimeSpan elapsedForString, elapsedForSb;
// string
sw.Restart();
var str = string.Empty;
for (int i = 0; i < iterationsCount; i++)
{
str += RandomStringGenerator.GetRandomString();
}
sw.Stop();
elapsedForString = sw.Elapsed;
// string builder
sw.Restart();
var sb = new StringBuilder(string.Empty);
for (int i = 0; i < iterationsCount; i++)
{
sb.Append(RandomStringGenerator.GetRandomString());
}
sw.Stop();
elapsedForSb = sw.Elapsed;
results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
}
// Results
results.Chart(r => r.Key)
.AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
.AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
.DumpInline();
}
static class RandomStringGenerator
{
static Random r;
static string[] strings;
public static void Setup(int testStringLength)
{
r = new Random(DateTime.Now.Millisecond);
strings = new string[10];
for (int i = 0; i < strings.Length; i++)
{
strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
}
}
public static string GetRandomString()
{
var indx = r.Next(0, strings.Length);
return strings[indx];
}
}
いつ使うか使わないかという微妙な境界線はないと思います。もちろん、誰かが黄金の状態を出すためにいくつかの広範なテストを実行しない限り。
私にとっては、2つの巨大な文字列を連結するだけの場合は、StringBuilderを使用しません。カウントが不確定なループがある場合は、ループが小さいカウントであっても、そうなる可能性があります。