161

これを行う C# の stringbuilder があるとします。

StringBuilder sb = new StringBuilder();
string cat = "cat";
sb.Append("the ").Append(cat).(" in the hat");
string s = sb.ToString();

それは次のものと同じくらい効率的か、それ以上に効率的ですか?

string cat = "cat";
string s = String.Format("The {0} in the hat", cat);

もしそうなら、なぜですか?

編集

いくつかの興味深い回答の後、私が求めていることをもう少し明確にするべきだったと気づきました。文字列を連結するのにどちらが速いか、ある文字列を別の文字列に挿入するのにどちらが速いかをあまり求めていませんでし

上記のどちらの場合でも、定義済みのテンプレート文字列の途中に 1 つ以上の文字列を挿入したいと考えています。

混乱させて申し訳ありません

4

12 に答える 12

148

注:この回答は、.NET 2.0 が現在のバージョンであったときに書かれました。これは、以降のバージョンには適用されない可能性があります。

String.FormatStringBuilderを内部的に使用します。

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }

    StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
    builder.AppendFormat(provider, format, args);
    return builder.ToString();
}

上記のコードは mscorlib のスニペットであるため、質問は「StringBuilder.Append()よりも高速ですStringBuilder.AppendFormat()」になります。

ベンチマークがなければ、おそらく、上記のコード サンプルは.Append(). しかし、それは推測です。適切な比較を得るために、2 つをベンチマークおよび/またはプロファイリングしてみてください。

このチャップ、Jerry Dixon はいくつかのベンチマークを行いました。

http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

更新しました:

悲しいことに、上記のリンクはその後死んでいます。ただし、Way Back Machine にはまだコピーがあります。

http://web.archive.org/web/20090417100252/http://jdixon.dotnetdevelopersjournal.com/string_concatenation_stringbuilder_and_stringformat.htm

結局のところ、文字列の書式設定が繰り返し呼び出されるかどうか、つまり、数百メガバイトのテキストに対して深刻なテキスト処理を行っているかどうか、またはユーザーがボタンを時々クリックしたときに呼び出されるかどうかによって異なります。大規模なバッチ処理ジョブを実行している場合を除き、私は String.Format に固執しますが、コードの読みやすさに役立ちます。パフォーマンスのボトルネックが疑われる場合は、コードにプロファイラーを貼り付けて、実際の場所を確認してください。

于 2008-08-09T15:57:15.970 に答える
45

MSDN ドキュメントから:

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

于 2008-08-09T14:36:45.290 に答える
12

いくつかの簡単なパフォーマンス ベンチマークを実行しました。10 回の実行で平均 100,000 回の操作を行った場合、最初の方法 (文字列ビルダー) は 2 番目の方法 (文字列形式) のほぼ半分の時間で済みました。

したがって、これがまれであれば問題ありません。ただし、これが一般的な操作である場合は、最初の方法を使用することをお勧めします。

于 2008-08-09T19:34:51.567 に答える
10

String.Formatの方が遅くなると思います-文字列を解析してから連結する必要があります。

いくつかのメモ:

  • 形式は、プロフェッショナルなアプリケーションでユーザーに表示される文字列の方法です。これにより、ローカリゼーションのバグが回避されます
  • 結果の文字列の長さが事前にわかっている場合は、StringBuilder(Int32)コンストラクターを使用して容量を事前定義します。
于 2008-08-09T17:09:10.880 に答える
8

ほとんどの場合、効率ではなく、この明快さが最大の関心事であると思います。大量のひもをくしゃくしゃにしたり、低電力のモバイル デバイス用に何かを構築したりしない限り、これはおそらく実行速度に大きな影響を与えることはありません。

かなり直線的な方法で文字列を作成している場合は、直接連結するか、StringBuilder を使用するのが最善の選択肢であることがわかりました。構築している文字列の大部分が動的である場合にこれをお勧めします。静的テキストはほとんどないため、最も重要なことは、将来更新が必要になった場合に備えて、動的テキストの各部分がどこに配置されているかを明確にすることです。

一方、2 つまたは 3 つの変数を含む静的テキストの大きなチャンクについて話している場合は、たとえ効率が少し悪くても、string.Format から得られる明快さは価値があると思います。今週初め、4 ページの文書の中央に 1 ビットの動的テキストを配置する必要があるときに、これを使用しました。連結された 3 つの部分を更新するよりも、1 つの部分である場合は、その大きなテキストのチャンクを更新する方が簡単です。

于 2008-08-09T14:40:00.650 に答える
8

string.Format があなたの考えを正確に実行しないという理由だけで、6 年後の Net45 でのテストの再実行を次に示します。

Concat は依然として最速ですが、実際には 30% 未満の差です。StringBuilder と Format の違いはわずか 5 ~ 10% です。テストを数回実行すると、20% の変動がありました。

ミリ秒、100 万回の反復:

  • 連結: 367
  • 各キーの新しい stringBuilder: 452
  • キャッシュされた StringBuilder: 419
  • string.Format: 475

ここで得た教訓は、パフォーマンスの違いは取るに足らないものであり、できるだけ単純で読みやすいコードを書くことをやめるべきではないということです。私のお金のために、これはしばしばですが、常にではありませんa + b + c

const int iterations=1000000;
var keyprefix= this.GetType().FullName;
var maxkeylength=keyprefix + 1 + 1+ Math.Log10(iterations);
Console.WriteLine("KeyPrefix \"{0}\", Max Key Length {1}",keyprefix, maxkeylength);

var concatkeys= new string[iterations];
var stringbuilderkeys= new string[iterations];
var cachedsbkeys= new string[iterations];
var formatkeys= new string[iterations];

var stopwatch= new System.Diagnostics.Stopwatch();
Console.WriteLine("Concatenation:");
stopwatch.Start();

for(int i=0; i<iterations; i++){
    var key1= keyprefix+":" + i.ToString();
    concatkeys[i]=key1;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("New stringBuilder for each key:");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2= new StringBuilder(keyprefix).Append(":").Append(i.ToString()).ToString();
    stringbuilderkeys[i]= key2;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("Cached StringBuilder:");
var cachedSB= new StringBuilder(maxkeylength);
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key2b= cachedSB.Clear().Append(keyprefix).Append(":").Append(i.ToString()).ToString();
    cachedsbkeys[i]= key2b;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

Console.WriteLine("string.Format");
stopwatch.Restart();

for(int i=0; i<iterations; i++){
    var key3= string.Format("{0}:{1}", keyprefix,i.ToString());
    formatkeys[i]= key3;
}

Console.WriteLine(stopwatch.ElapsedMilliseconds);

var referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway= concatkeys.Union(stringbuilderkeys).Union(cachedsbkeys).Union(formatkeys).LastOrDefault(x=>x[1]=='-');
Console.WriteLine(referToTheComputedValuesSoCompilerCantOptimiseTheLoopsAway);
于 2015-01-12T12:52:46.433 に答える
6

String.Format内部で使用StringBuilderされるため、論理的には、オーバーヘッドが増えるためにパフォーマンスが少し低下するという考えにつながります。ただし、単純な文字列連結は、1 つの文字列を他の 2 つの文字列の間に挿入する最速の方法です。この証拠は、何年も前にリコ マリアーニが最初のパフォーマンス クイズで示しました。単純な事実は、文字列部分の数がわかっている場合 (制限なし — 常に 1000 部分であることがわかっている限り、1000 部分を連結できます)、連結は常にStringBuilderorよりも高速ですString.Format。これらは、単一のメモリ割り当てと一連のメモリ コピーで実行できます。これが証拠です。

String.Concat最終的に を呼び出すいくつかのメソッドの実際のコードは次のFillStringCheckedとおりです。これはポインタを使用してメモリをコピーします (Reflector を介して抽出)。

public static string Concat(params string[] values)
{
    int totalLength = 0;

    if (values == null)
    {
        throw new ArgumentNullException("values");
    }

    string[] strArray = new string[values.Length];

    for (int i = 0; i < values.Length; i++)
    {
        string str = values[i];
        strArray[i] = (str == null) ? Empty : str;
        totalLength += strArray[i].Length;

        if (totalLength < 0)
        {
            throw new OutOfMemoryException();
        }
    }

    return ConcatArray(strArray, totalLength);
}

public static string Concat(string str0, string str1, string str2, string str3)
{
    if (((str0 == null) && (str1 == null)) && ((str2 == null) && (str3 == null)))
    {
        return Empty;
    }

    if (str0 == null)
    {
        str0 = Empty;
    }

    if (str1 == null)
    {
        str1 = Empty;
    }

    if (str2 == null)
    {
        str2 = Empty;
    }

    if (str3 == null)
    {
        str3 = Empty;
    }

    int length = ((str0.Length + str1.Length) + str2.Length) + str3.Length;
    string dest = FastAllocateString(length);
    FillStringChecked(dest, 0, str0);
    FillStringChecked(dest, str0.Length, str1);
    FillStringChecked(dest, str0.Length + str1.Length, str2);
    FillStringChecked(dest, (str0.Length + str1.Length) + str2.Length, str3);
    return dest;
}

private static string ConcatArray(string[] values, int totalLength)
{
    string dest = FastAllocateString(totalLength);
    int destPos = 0;

    for (int i = 0; i < values.Length; i++)
    {
        FillStringChecked(dest, destPos, values[i]);
        destPos += values[i].Length;
    }

    return dest;
}

private static unsafe void FillStringChecked(string dest, int destPos, string src)
{
    int length = src.Length;

    if (length > (dest.Length - destPos))
    {
        throw new IndexOutOfRangeException();
    }

    fixed (char* chRef = &dest.m_firstChar)
    {
        fixed (char* chRef2 = &src.m_firstChar)
        {
            wstrcpy(chRef + destPos, chRef2, length);
        }
    }
}

それで:

string what = "cat";
string inthehat = "The " + what + " in the hat!";

楽しみ!

于 2009-05-23T21:33:21.450 に答える
3

また、最速は次のようになります。

string cat = "cat";
string s = "The " + cat + " in the hat";
于 2008-08-09T19:51:24.607 に答える
0

それは本当にあなたの使用パターンに依存します。
との間の詳細なベンチマークはstring.Join、次の場所にstring,Concatありstring.Formatます: String.Format Isn't Compatible for Intensive Logging

于 2010-08-01T18:40:40.097 に答える
0

それは本当に依存します。連結が少ない小さな文字列の場合、文字列を追加するだけで実際には高速になります。

String s = "String A" + "String B";

ただし、より大きな文字列 (非常に大きな文字列) の場合は、StringBuilder を使用する方が効率的です。

于 2008-08-09T14:31:51.117 に答える
0

上記のどちらの場合でも、定義済みのテンプレート文字列の途中に 1 つ以上の文字列を挿入したいと考えています。

その場合、 String.Format が最も速いことをお勧めします。これは、まさにその目的のために設計されているためです。

于 2008-08-09T16:29:55.513 に答える
-1

String.Format は連結用に設計されていないため、日付などのさまざまな入力の出力をフォーマットするための設計であったため、そうしないことをお勧めします。

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
于 2008-08-09T14:28:47.550 に答える