Wann wird StringBuilder verwendet?


82

Ich verstehe die Vorteile von StringBuilder.

Wenn ich jedoch 2 Zeichenfolgen verketten möchte, gehe ich davon aus, dass es besser (schneller) ist, dies ohne StringBuilder zu tun. Ist das richtig?

Ab wann (Anzahl der Zeichenfolgen) ist es besser, StringBuilder zu verwenden?


1
Ich glaube, dass dies bereits behandelt wurde.
Mark Schultheiss


Antworten:


79

Ich empfehle Ihnen wärmstens, die traurige Tragödie des Mikrooptimierungstheaters von Jeff Atwood zu lesen .

Es behandelt Simple Concatenation vs. StringBuilder vs. andere Methoden.

Wenn Sie nun einige Zahlen und Grafiken sehen möchten, folgen Sie dem Link;)


+1 für ja ! Die Zeit, die Sie damit verbringen, sich darüber Gedanken zu machen, ist die Zeit, die Sie damit verbringen, nichts zu tun, was tatsächlich wichtig sein könnte.
Greg D

8
Ihre Lesart ist jedoch falsch: Es spielt in vielen Fällen keine Rolle, wenn keine Schleife beteiligt ist, in anderen Fällen kann es jedoch VIEL wichtig sein
Peter

1
Ich habe die Bearbeitung entfernt, weil es sich bei einer akzeptierten Antwort nur um falsche Informationen handelte.
Peter

2
und nur um zu zeigen, wie wichtig es ist, aus dem Artikel, auf den Sie sich beziehen: "In den meisten Sprachen, in denen Müll gesammelt wird, sind Zeichenfolgen unveränderlich: Wenn Sie zwei Zeichenfolgen hinzufügen, wird der Inhalt beider kopiert. Wenn Sie weiter hinzufügen, entsteht diese Schleife Es wird jedes Mal mehr und mehr Speicher zugewiesen. Dies führt direkt zu einer schrecklichen quadradischen n2-Leistung "
Peter

1
Warum ist das die akzeptierte Antwort? Ich denke nicht, dass es eine gute Antwort ist, einfach einen Link zu löschen und "go read this" zu sagen
Kolob Canyon

44

Aber wenn ich 2 Strings zusammenfassen möchte, gehe ich davon aus, dass es besser (schneller) ist, dies ohne StringBuilder zu tun. Ist das richtig?

Das ist in der Tat richtig, Sie können herausfinden, warum genau sehr gut erklärt auf:

http://www.yoda.arachsys.com/csharp/stringbuilder.html

Zusammenfassend: Wenn Sie Saiten auf einmal zusammenfassen können, gehen Sie wie

var result = a + " " + b  + " " + c + ..

Sie sind ohne StringBuilder besser dran, da nur eine Kopie erstellt wird (die Länge der resultierenden Zeichenfolge wird zuvor berechnet.);

Für Struktur wie

var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

Jedes Mal werden neue Objekte erstellt, daher sollten Sie dort StringBuilder in Betracht ziehen.

Am Ende fasst der Artikel folgende Faustregeln zusammen:

Faustregeln

Wann sollten Sie StringBuilder verwenden und wann sollten Sie die String-Verkettungsoperatoren verwenden?

  • Verwenden Sie StringBuilder auf jeden Fall, wenn Sie in einer nicht trivialen Schleife verketten - insbesondere, wenn Sie nicht genau wissen (zur Kompilierungszeit), wie viele Iterationen Sie durch die Schleife durchführen werden. Wenn Sie beispielsweise eine Datei zeichenweise lesen und mit dem Operator + = eine Zeichenfolge erstellen, ist dies möglicherweise ein Leistungsselbstmord.

  • Verwenden Sie auf jeden Fall den Verkettungsoperator, wenn Sie (lesbar) alles angeben können, was in einer Anweisung verkettet werden muss. (Wenn Sie eine Reihe von Dingen verketten müssen, sollten Sie String.Concat explizit aufrufen - oder String.Join, wenn Sie ein Trennzeichen benötigen.)

  • Haben Sie keine Angst, Literale in mehrere verkettete Bits aufzuteilen - das Ergebnis ist das gleiche. Sie können die Lesbarkeit verbessern, indem Sie beispielsweise ein langes Literal in mehrere Zeilen aufteilen, ohne die Leistung zu beeinträchtigen.

  • Wenn Sie die Zwischenergebnisse der Verkettung für etwas anderes als die nächste Iteration der Verkettung benötigen, hilft Ihnen StringBuilder nicht weiter. Wenn Sie beispielsweise einen vollständigen Namen aus einem Vor- und einem Nachnamen erstellen und am Ende eine dritte Information (möglicherweise den Spitznamen) hinzufügen, profitieren Sie nur dann von der Verwendung von StringBuilder, wenn Sie dies nicht tun Benötigen Sie die Zeichenfolge (Vorname + Nachname) für einen anderen Zweck (wie im Beispiel, in dem ein Personenobjekt erstellt wird).

  • Wenn Sie nur ein paar Verkettungen zu erledigen haben und diese wirklich in separaten Anweisungen ausführen möchten, spielt es keine Rolle, in welche Richtung Sie gehen. Welcher Weg effizienter ist, hängt von der Anzahl der Verkettungen, der Größe der beteiligten Zeichenfolgen und der Reihenfolge ab, in der sie verkettet sind. Wenn Sie wirklich glauben, dass dieser Code ein Leistungsengpass ist, können Sie ihn in beide Richtungen profilieren oder bewerten.


13

System.String ist ein unveränderliches Objekt. Dies bedeutet, dass bei jeder Änderung des Inhalts eine neue Zeichenfolge zugewiesen wird. Dies kostet Zeit (und Speicher?). Mit StringBuilder ändern Sie den tatsächlichen Inhalt des Objekts, ohne einen neuen zuzuweisen.

Verwenden Sie StringBuilder, wenn Sie viele Änderungen an der Zeichenfolge vornehmen müssen.


8

Nicht wirklich ... Sie sollten StringBuilder verwenden, wenn Sie große Zeichenfolgen verketten oder wenn Sie viele Verkettungen haben, wie in einer Schleife.


1
Das ist falsch. Sie sollten StringBuildernur verwenden, wenn die Schleife oder die Verkettung ein Leistungsproblem für die Spezifikationen darstellt.
Alex Bagnolini

2
@ Alex: Ist das nicht immer der Fall? ;) Nein, im Ernst, ich habe StringBuilder immer für die Verkettung innerhalb einer Schleife verwendet ... obwohl meine Schleifen alle mehr als 1k Iterationen haben ... @Binary: Normalerweise sollte das kompiliert werden string s = "abcd", zumindest ist das das Letzte Ich habe gehört ... aber mit Variablen wäre es höchstwahrscheinlich Concat.
Bobby

1
Tatsache ist: Es ist fast IMMER NICHT der Fall. Ich habe immer String-Operatoren verwendet a + "hello" + "somethingelse"und musste mich nie darum kümmern. Wenn es zu einem Problem wird, verwende ich StringBuilder. Aber ich habe mir überhaupt keine Sorgen gemacht und weniger Zeit damit verbracht, es zu schreiben.
Alex Bagnolini

3
Bei großen Zeichenfolgen gibt es absolut keinen Leistungsvorteil - nur bei vielen Verkettungen.
Konrad Rudolph

1
@Konrad: Sind Sie sicher, dass es keinen Leistungsvorteil gibt? Jedes Mal, wenn Sie große Zeichenfolgen verketten, kopieren Sie eine große Datenmenge. Jedes Mal, wenn Sie kleine Zeichenfolgen verketten, kopieren Sie nur eine kleine Datenmenge.
LukeH

6
  • Wenn Sie Zeichenfolgen in einer Schleife verketten, sollten Sie StringBuilder anstelle der regulären Zeichenfolge verwenden
  • Wenn es sich um eine einzelne Verkettung handelt, sehen Sie möglicherweise überhaupt keinen Unterschied in der Ausführungszeit

Hier ist eine einfache Test-App, um den Punkt zu beweisen:

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();
    }
}

Ergebnisse:

  • 30'000 Iterationen

    • String - 2000 ms
    • StringBuilder - 4 ms
  • 1000 Iterationen

    • String - 2 ms
    • StringBuilder - 1 ms
  • 500 Iterationen

    • String - 0 ms
    • StringBuilder - 0 ms

5

Umschreiben

Dann sollst du bis drei zählen, nicht mehr und nicht weniger. Drei soll die Zahl sein, die du zählen sollst, und die Zahl der Zählung soll drei sein. Vier sollst du nicht zählen, und du sollst auch nicht zwei zählen, außer dass du dann zu drei übergehst. Sobald die Nummer drei, die dritte Nummer, erreicht ist, dann lobst du deine Heilige Handgranate von Antiochia

Im Allgemeinen verwende ich den String Builder für jeden Codeblock, der zur Verkettung von drei oder mehr Strings führen würde.


Es kommt darauf an: Concetanation erstellt nur eine Kopie: "Russell" + "" + Steen + "." Erstellt nur eine Kopie, da die Länge der Zeichenfolge zuvor berechnet wird. Erst wenn Sie Ihre Verkettung aufteilen müssen, sollten Sie über einen Builder nachdenken
Peter

4

Es gibt keine endgültige Antwort, nur Faustregeln. Meine persönlichen Regeln lauten ungefähr so:

  • Wenn Sie in einer Schleife verketten, verwenden Sie immer a StringBuilder.
  • Wenn die Zeichenfolgen groß sind, verwenden Sie immer a StringBuilder.
  • Wenn der Verkettungscode auf dem Bildschirm aufgeräumt und lesbar ist, ist er wahrscheinlich in Ordnung.
    Wenn nicht, verwenden Sie a StringBuilder.

Ich weiß, dass dies ein altes Thema ist, aber ich kenne mich nur mit Lernen aus und wollte wissen, was Sie als "große Saite" betrachten?
MatthewD

4

Wenn ich jedoch zwei Zeichenfolgen verketten möchte, gehe ich davon aus, dass dies ohne StringBuilder besser und schneller ist. Ist das richtig?

Ja. Noch wichtiger ist jedoch , dass die Verwendung einer Vanille in solchen Situationen weitaus besser lesbar istString . Die Verwendung in einer Schleife ist dagegen sinnvoll und kann ebenso lesbar sein wie die Verkettung.

Ich würde mich vor Faustregeln hüten, die eine bestimmte Anzahl von Verkettungen als Schwelle angeben. Die Verwendung in Schleifen (und nur in Schleifen) ist wahrscheinlich genauso nützlich, leichter zu merken und sinnvoller.


"Ich würde mich vor Faustregeln hüten, die eine bestimmte Anzahl von Verkettungen als Schwelle angeben" <this. Denken Sie auch nach der Anwendung des gesunden Menschenverstandes an die Person, die in 6 Monaten zu Ihrem Code zurückkehrt.
Phil Cooper

3

Solange Sie die Anzahl der Verkettungen (a + b + c ...) physisch eingeben können, sollte dies keinen großen Unterschied machen. N im Quadrat (bei N = 10) ist eine 100-fache Verlangsamung, die nicht schlecht sein sollte.

Das große Problem ist, wenn Sie Hunderte von Zeichenfolgen verketten. Bei N = 100 erhalten Sie eine 10000-fache Verlangsamung. Welches ist ziemlich schlecht.


3

Da es schwierig ist, eine Erklärung dafür zu finden, die weder von Meinungen beeinflusst noch von einem Kampf der Stolz gefolgt wird, habe ich mir überlegt, ein bisschen Code auf LINQpad zu schreiben, um dies selbst zu testen.

Ich fand heraus, dass die Verwendung kleiner Zeichenfolgen anstelle von i.ToString () die Antwortzeiten ändert (sichtbar in kleinen Schleifen).

Der Test verwendet verschiedene Iterationssequenzen, um Zeitmessungen in vernünftig vergleichbaren Bereichen zu halten.

Ich werde den Code am Ende kopieren, damit Sie ihn selbst ausprobieren können (results.Charts ... Dump () funktioniert außerhalb von LINQPad nicht).

Ausgabe (X-Achse: Anzahl der getesteten Iterationen, Y-Achse: Zeit in Ticks):

Iterationssequenz: 2, 3, 4, 5, 6, 7, 8, 9, 10 Iterationssequenz: 2, 3, 4, 5, 6, 7, 8, 9, 10

Iterationssequenz: 10, 20, 30, 40, 50, 60, 70, 80 Iterationssequenz: 10, 20, 30, 40, 50, 60, 70, 80

Iterationssequenz: 100, 200, 300, 400, 500 Iterationssequenz: 100, 200, 300, 400, 500

Code (geschrieben mit 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

Ich glaube nicht, dass es eine feine Linie zwischen dem Zeitpunkt und dem Nichtgebrauch gibt. Es sei denn natürlich, jemand hat einige umfangreiche Tests durchgeführt, um mit den goldenen Bedingungen herauszukommen.

Für mich werde ich StringBuilder nicht verwenden, wenn ich nur 2 große Zeichenfolgen verkette. Wenn es eine Schleife mit einer nicht deterministischen Anzahl gibt, bin ich es wahrscheinlich, auch wenn die Schleife eine kleine Anzahl sein könnte.


In der Tat wäre es völlig falsch, StringBuilder zu verwenden, um 2 Strings zu konzentrieren, aber das hat nichts mit Perf zu tun. Testen - das heißt einfach, es für das Falsche zu verwenden.
Marc Gravell

1

Eine einzelne Verkettung lohnt sich nicht mit einem StringBuilder. Ich habe normalerweise 5 Verkettungen als Faustregel verwendet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.