Donnerstag, 9. September 2010

String vs Stringbuilder

Zeichenketten werden in C# meistens mit dem Datentyp string dargestellt.
Wird ein String einmal initialisiert, ist er nicht mehr veränderbar. Operationen, die auf Strings ausgeführt werden (wie zum Beispiel Replace() oder Trim()), lassen das Original unverändert und ändern stattdessen eine Kopie, welche sie dann zurückgeben.
Um im .Net Framework zur Laufzeit änderbare Strings zu benutzen, ist die Klasse StringBuilder nötig.
Instanzen dieser werden wie Klassen über den new Operator angelegt und können dann dynamisch verändert werden.
Operationen werden nun direkt am Objekt durchgeführt:

StringBuilder DynamicString = new StringBuilder("Ersetze mich");
DynamicString.Replace("Ersetze mich", "Ersetzt.");
MessageBox.Show(DynamicString.ToString());

DynamicString enthält nach dem Ersetzen den Wert "Ersetzt", bei einem String wäre dies nicht der Fall.
Wie man an dem Code sehen kann, muss eine Zeichenkette vom Typ StringBuilder explizit in einen String konvertiert werden, wenn ein String verlangt wird.
Diese technischen Details der Implementierungen werden allerdings die wenigsten interessieren, die Klasse StringBuilder hat noch eine sehr viel bessere Eigenschaft, die sie sehr interessant macht: Die Geschwindigkeit bei der Verarbeitung.
Die Verarbeitung von Zeichenketten über den Typ String ist im Allgemeinen recht langsam.
Liest man beispielsweise eine lange Textdatei ein und schreibt den Inhalt in einen String, nimmt diese Aktion sehr viel Zeit in Anspruch.
Das folgende Codebeispiel verkettet 100000-mal das Wort "zeichenkette":

string OrdinaryString = "";
for (int i = 0; i < 100000; i++)
{
    OrdinaryString += "zeichenkette";
}

Das Programm benötigt zur Ausführung bei Verwendung des Typs String auf meinem Laptop mehr als 1 Minute.
Die gleiche Aufgabe schafft es bei Verwendung des Typs StringBuilder ohne merkliche Zeitverzögerung, das Ergebnis ist sofort da!

StringBuilder BetterString = new StringBuilder();
for (int i = 0; i < 100000; i++)
{
    BetterString.Append("zeichenkette");
}

Bei der Arbeit mit längeren Zeichenketten sollte man also auf jeden Fall die Klasse StringBuilder verwenden, denn diese bringt einen unglaublichen Zeitvorteil mit sich - allerdings muss auch gesagt werden, dass String bei ziemlich kurzen Zeichenketten effektiver ist.
Den gleichen "Trick" kann man auch anwenden, wenn man längere Texte in eine TextBox schreiben möchte. Die Eigenschaft Text des Steuerelements ist ebenfalls als String umgesetzt. Man sollte daher den längeren Text zuerst in einem StringBuilder zusammenbauen und dann diesen als Ganzes dem Textfeld zuweisen.

Kommentare:

  1. Wobei gesagt werden sollte dass es nicht primär an den "kurzen" oder "langen" strings liegt, sondern an der Massenhaften Neuerzeugung von Objekten des Structs String. In deinem Beispiel werden 99.999 String Instanzen umsonst erzeugt.
    StringBuilder ist daher nur schneller wenn viele Strings zusammengefasst werden sollten.

    AntwortenLöschen
  2. Hallo, wenn Strings vereint werden sollen empfiehlt sich String.Concat. Mit der Methode könenn 2,3,4 Strings miteinander verbunden werden. Es ist auch nochmal ein Zacken schneller, als wenn man Strings wie folgt verküpft

    s = s1+s2+s3;
    String.Concat( s1,s2,s3);

    In der Größenordnung verliert der StringBuilder seine Performance, da am Ende die ToString Methode ebenfalls Zeitfrisst. Bei Verknüpfungen in Schleifen macht der Biolder Sinn, da nicht jedesmal ein neues String Objekt angelegt wird.

    AntwortenLöschen
    Antworten
    1. Sehr guter Hinweis bzgl. String.Concat.

      Eine Variante, in der alle betreffenden Strings in einer String-Collection gesammelt werden, diese dann abschließend in ein String-Array überführt wird und dann der Inhalt per String.Concat zusammengeführt wird, ist noch schneller als der StringBuilder. Der Geschwindigkeitsgewinn ist zwar marginal, aber dennoch messbar. Je mehr Strings berücksichtigt werden, desto interessanter dürfte der Vorteil werden.

      Löschen
  3. Wenn wir gerade bei Strings sind, da hätte ich ein anderes Anliegen.

    Es ist nämlich so, wenn ich zum Beispiel eine Trennlinie (z. B. ------------------ ) einfügen will, dann ist es ja unsinnig, die Bindestriche alle zu tippen.

    Der Syntax ist anscheinend so einfach, sodass ich nichts darüber finden kann. Auch nicht in Fachbüchern.

    Ich weiß nur, dass ich so ungefähr vorgehen muss:

    Console.WriteLine("-", 30);

    Aber das ist auf jeden Fall falsch, das weiß ich.
    Wäre nett, wenn jemand mir helfen könnte!

    AntwortenLöschen
  4. Also die Frage verstehe ich jetzt nicht so ganz, tut mir Leid. Möchtest du einfach 30 Bindestriche nacheinander auf der Konsole angeben?
    Die obige von dir angemerkte Schreibweise kenne ich leider auch nicht, ganz einfach geht natürlich immer:

    for (int i=0;i<30;i++) {
    Console.WriteLine("-");
    }

    AntwortenLöschen
  5. Genau, 30 Bindestriche nacheinander.

    Mit der Schleife umgeht man das natürlich auf geschickte Weise. :)

    Aber inzwischen bin ich doch fündig geworden.
    Der korrekte Code dafür lautet:

    string linie = new string('-', 30);

    Ich hoffe, ich hab dich mit meiner Frage nicht ganz drucheinander gebracht. :)

    Und danke trotzdem fürs Antworten!

    AntwortenLöschen
  6. Das ist ja cool, kannte ich gar nicht.
    Darf ich das als eigenständigen Tipp veröffentlichen? :-)

    AntwortenLöschen
  7. Wieder was gelernt! ;)

    Klaro darfst du das, wenn du im Gegenzug auf meine Seite gehst:

    www.marks-softwareschmiede.npage.de

    Wäre nett! ;)

    AntwortenLöschen
  8. Hey,
    schicke Seite!
    Ich wünsche dir damit viel Erfolg, ich werde demnächst noch vorbeischauen.
    Grüße
    Oliver

    AntwortenLöschen
  9. Dankeschön!
    Die Seite könnte evtl. ganz groß rauskommen mit viel Glück. :)

    AntwortenLöschen
  10. Tipp: Die Seite ist jetzt unter
    http://marks-softwareschmiede.jimdo.com/
    verfügbar!!!

    AntwortenLöschen