String vs. StringBuilder


215

StringStringBuilderStringBuilder変更可能)の違いは理解していますが、2つのパフォーマンスに大きな違いはありますか?

私が取り組んでいるプログラムには、大文字と小文字が区別される文字列の追加(500以上)がたくさんあります。StringBuilderより良い選択を使用していますか?

回答:


236

はい、パフォーマンスの違いは重要です。KB記事「Visual C#で文字列連結のパフォーマンスを向上させる方法」を参照してください。

私は常に明確にするために最初にコーディングを試み、その後パフォーマンスを最適化するように後で試みました。これは、逆に行うよりもはるかに簡単です。ただし、2つのアプリケーションのパフォーマンスに大きな違いがあることを知ったので、今はもう少し慎重に考えています。

幸い、コードでパフォーマンス分析を実行して、どこに時間を費やしているかを確認しStringBuilder、必要な場所で使用するように変更するのは比較的簡単です。


44
経験則として、変更しない場合はStringsを使用し、変更する場合はStringBuilderを使用します。
Tim

31
私はこの答え、特にパフォーマンスの前に明確にするためのコードへのアドバイスをとても気に入っています。開発者として、私たちはコードを書くのと同じくらい多くの時間をコードの読み取りに費やしています。
スコットローレンス、

無法者:StackOverflowを正しく理解していれば、それは個別に投票される独自の回答になるはずです。
Jay Bazuzi 2008

2
私の経験に基づいて、10〜15の文字列を連結しようとすると、文字列を使用できますが、そうでない場合は。文字列の数がそれより多い場合は、文字列ビルダーを使用します
Peeyush

3
それはまだ実際に私は次のようにテストを参照するために、どのようにいずれかを使用し、それに依存マイクロ最適化シアターの悲しい悲劇-ホラーコーディング
エリック・フィリップス

56

次のようなものがある場合、ジリアンが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();

5
C#コンパイラが2番目のサンプルを最初のものとは異なる方法で処理する必要がある理由はありません。特に、各行の終わりに文字列を生成する義務はありません。コンパイラはあなたが言うように動作するかもしれませんが、そうする義務はありません。
CodesInChaos 2013

@CodesInChaos、不変文字列変数が各行の終わりに具体的に割り当てられていますが、それは文字列を生成する義務を生み出しませんか?ただし、個々の行を個別に処理する理由はない(そして、そうであるかどうかはわかりません)という点に同意します。パフォーマンスの損失は再割り当てによって発生するため、問題ではありません。
Saeb Amini、2015

@SaebAmini- aがローカル変数であり、それが参照するオブジェクトが他の変数(別のスレッドからアクセス可能である可能性がある)に割り当てられていない場合、優れたオプティマイザは、このシーケンス中に他のコードがaアクセスしていないと判断できます線; 問題の最終的な値のみa。そのため、これらの3行のコードを、作成されたかのように扱うことができa = b + c + d;ます。
ToolmakerSteve 2018年

29

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アプローチよりもパフォーマンスが優れています(疑わしい場合は...リフレクターを確認してください!)


17
あなたがそれをどのように知っているか、それを確認する方法をあなたが言っていたらいいのに。
ChrisW、2009

2
リフレクターでパフォーマンスを検証しない:リリースコードのタイミングでパフォーマンスを検証し、プロファイラーで分析して、リフレクターで説明を求めます。
アルビンSunnanbo

3
String.Format()を使用して少数の小さな文字列を連結することを検討してください。特に、メッセージをフォーマットしてユーザーに表示することが目的です。
ゲイリーキンデル2010

1
これは非常に悪い情報です。( "string myStringの方がパフォーマンスが高い")まったく当てはまりません。
Tom Stickel 2013

3
この回答の情報は正しくありません。StringBuilderは、同じステートメントで行われる連結よりも少し高速です。ただし、数十万回実行した場合にのみ、2つの違いがわかります(Source)。ソースで述べたように、「それは問題ではありません!」
David Sherret、2014年

25

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方がはるかに高速であるように見えます。


24

このベンチマークは、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に、そのバッファーをコンストラクター内でどれだけ大きくするかを指示できます。


2
マイナーニット:「13の文字列オブジェクトを作成し、そのうちの12は役に立たなかった」と言うのは少し奇妙であり、次にStringBuilderがこの問題を修正すると言います。結局、6つの文字列を作成する以外に選択肢はありません。彼らはからi.ToString()です。したがって、StringBuilderでは、6 + 1の文字列を作成する必要があります。13個の文字列の作成を7個の文字列の作成に減らしました。しかし、それはまだそれを見るには間違った方法です。6つの数値文字列の作成は関係ありません。結論:あなたは単にによって作成された6つの文字列に言及すべきではありませんでしたi.ToString()。それらは効率比較の一部ではありません。
ToolmakerSteve 2018年

12

はい、StringBuilder文字列に対して繰り返し操作を実行する際のパフォーマンスが向上します。これは、すべての変更が単一のインスタンスに対して行われるため、などの新しいインスタンスを作成する代わりに時間を大幅に節約できるためStringです。

文字列対文字列ビルダー

  • String

    1. System名前空間の下
    2. 不変(読み取り専用)インスタンス
    3. 値の継続的な変化が発生すると、パフォーマンスが低下します
    4. スレッドセーフ
  • StringBuilder (可変文字列)

    1. System.Text名前空間の下
    2. 可変インスタンス
    3. 既存のインスタンスに新しい変更が加えられるため、パフォーマンスが向上します

dotnet mobの記事を強くお勧めします:StringとC#のStringBuilder

関連スタックオーバーフローの質問:文字列がC#で変更されない場合の文字列の可変性?


12

文字列対文字列ビルダー:

最初に知っておくべきことは、これらの2つのクラスがどのアセンブリに存在するかということです。

そう、

文字列System名前空間に存在します。

そして

StringBuilderSystem.Text名前空間に存在します。

以下のための文字列の宣言:

System名前空間を含める必要があります。このようなもの。 Using System;

そして

以下のためのStringBuilderの宣言:

System.text名前空間を含める必要があります。このようなもの。 Using System.text;

今実際の質問に来てください。

stringStringBuilderの違いは何ですか?

これら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オブジェクトが多くないのは、なぜ変更される可能性があるかです。それは時間の経過とともに変化するという意味で変更可能ですか?

違い:

  • StringはSystem名前空間にあり、StringbuilderはSystem.Text名前空間にあります。
  • stringBuilderが変更可能である場合、stringは不変です。

8

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

5

StringまたはStringBuilderオブジェクトの連結操作のパフォーマンスは、メモリ割り当てが発生する頻度に依存します。String連結操作は常にメモリを割り当てますが、StringBuilder連結操作は、StringBuilderオブジェクトバッファが小さすぎて新しいデータを収容できない場合にのみメモリを割り当てます。したがって、固定数のStringオブジェクトを連結する場合、連結操作にはStringクラスが適しています。その場合、個々の連結操作は、コンパイラーによって1つの操作に結合されることさえあります。StringBuilderオブジェクトは、任意の数の文字列が連結される場合の連結操作に適しています。たとえば、ループがランダムな数のユーザー入力の文字列を連結する場合などです。

出典:MSDN


3

StringBuilder 多くの非定数値から文字列を作成するのに適しています。

HTMLまたはXMLドキュメントの複数行の値やその他のテキストのチャンクなど、多くの定数値から文字列を作成している場合、ほとんどすべてのコンパイラーが同じ文字列に追加するだけで済むようになります。 「一定の折りたたみ」、一定の操作がたくさんあるときに解析ツリーを削減するプロセス(これは、のようなものを書くときにも使用されますint minutesPerYear = 24 * 365 * 60)。また、非定数値が互いに追加されている単純なケースの場合、.NETコンパイラーは、コードを実行するのと同じようなものに削減しますStringBuilder

ただし、コンパイラーで追加をより簡単なものに削減できない場合は、が必要になりますStringBuilder。fizchが指摘するように、それはループ内で発生する可能性が高くなります。


2

一緒に追加する必要がある文字列が4つ以上ある場合は、StringBuilderの方が速いと思います。それに加えて、AppendLineのようなクールなことができます。


2

.NETでは、StringBuilderは文字列を追加するよりも高速です。Javaでは、文字列を追加するときに内部でStringBufferを作成するだけなので、実際には違いはないと確信しています。彼らがなぜ.NETでこれをまだ行っていないのか、私にはわかりません。



2

連結に文字列を使用すると、実行時にのオーダーで複雑になる可能性がありますO(n^2)

を使用するとStringBuilder、実行する必要のあるメモリのコピーが大幅に少なくなります。ではStringBuilder(int capacity)、あなたが最終的にどのように大規模な見積もることができる場合は、パフォーマンスを向上させることができますStringになるだろう。正確ではない場合でも、おそらくStringBuilder数倍の容量を増やすだけでパフォーマンスも向上します。


2

文字列ストレージに使用する前のEnsureCapacity(int capacity)インスタンスでメソッド呼び出しを使用することで、パフォーマンスが大幅に向上しましたStringBuilder。私は通常、インスタンス化後のコード行でそれを呼び出します。次のStringBuilderようにインスタンス化する場合と同じ効果があります。

var sb = new StringBuilder(int capacity);

この呼び出しにより、必要なメモリが事前に割り当てられます。これにより、複数のAppend()操作中のメモリ割り当てが少なくなります。どのくらいのメモリが必要になるかを知識に基づいて推測する必要がありますが、ほとんどのアプリケーションでは、これはそれほど難しくありません。私は通常、メモリが少なすぎる側でエラーを起こします(1k程度です)。


インスタンス化のEnsureCapacity直後に呼び出す必要はありませんStringBuilder。次のStringBuilderようにインスタンス化するだけですvar sb = new StringBuilder(int capacity)
David FerenczyRogožan2016年

素数を使うと役立つと言われました。
Karl Gjertsen、2016年

1

以前の回答に加えて、このような問題を考えるときに常に最初に行うことは、小さなテストアプリケーションを作成することです。このアプリ内で、両方のシナリオのタイミングテストを実行し、どちらが速いかを実際に確認してください。

私見、500以上の文字列エントリを追加するには、間違いなくStringBuilderを使用する必要があります。


1

StringとStringBuilderはどちらも実際には不変です。StringBuilderには、そのサイズをより効率的に管理できるようにするバッファが組み込まれています。StringBuilderのサイズを変更する必要があるのは、ヒープに再割り当てするときです。デフォルトのサイズは16文字ですが、これはコンストラクターで設定できます。

例えば。

StringBuilder sb = new StringBuilder(50);


2
イミュータブルの意味を理解しているかわかりません。文字列は不変であり、変更できません。「変更された」とは、実際には、古い値を見つけるためのポインタがなくても、古い値がヒープに残っているということです。StringBuilder(可変)はheapの参照型であり、ヒープへのポインタが変更され、この変更のためのスペースの割り当てが変更されます。
トムスティッケル2014年

1

文字列の連結には、さらにコストがかかります。Javaでは、必要に応じてStringBufferまたはStringBuilderを使用できます。同期されたスレッドセーフな実装が必要な場合は、StringBufferを使用してください。これは文字列連結よりも高速です。

同期またはスレッドセーフの実装が必要ない場合は、StringBuilderを使用してください。これは、文字列の連結よりも高速であり、同期化のオーバーヘッドがないため、StringBufferよりも高速です。


0

StringBuilderがおそらく望ましいでしょう。その理由は、将来必要になる追加のための余地を残すために、現在必要な(文字数を設定する)よりも多くのスペースを割り当てるためです。次に、現在のバッファーに収まる将来の追加では、メモリ割り当てやガベージコレクションを必要としないため、コストが高くなる可能性があります。一般に、私は複雑な文字列の連結または複数の書式設定にStringBuilderを使用し、データが完了したときに通常の文字列に変換し、再び不変オブジェクトが必要になります。


0

多くの文字列連結を行う場合は、StringBuilderを使用します。文字列と連結する場合は、毎回新しい文字列を作成し、より多くのメモリを消費します。

アレックス


0

一般的な経験則として、文字列の値を複数回設定する必要がある場合、または文字列に追加がある場合は、文字列ビルダーである必要があります。私は、成長を続けていると思われる巨大なメモリフットプリントを持つ文字列ビルダーについて学ぶ前に、過去に作成したアプリケーションを見てきました。これらのプログラムを変更して文字列ビルダーを使用すると、メモリ使用量が大幅に削減されます。今、私は文字列ビルダーに誓います。



0

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追加を終了しました。


1
文字列ビルダーが非常に強力な場合、なぜ文字列が存在するのか。文字列を完全に消去しませんか。私がこの質問をしたのは、StringBuilderに対するStringの利点
Unbreakable

-2

StringBuilderは、メモリの観点から、パフォーマンスが向上します。処理に関しては、実行時間の違いはごくわずかです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.