String vs. StringBuilder

Frank Eller     28.06.2017    20:17

Learning

Die Problematik String vs. StringBuilder ist so alt wie das .NET Framework selbst. Für langjährige Programmierer ist der Unterschied klar, weshalb auch niemand mehr darauf eingeht. Ich wurde letztens gefragt, worin der Unterschied bestehe (die betreffende Person sollte im Code den StringBuilder verwenden, verstand aber nicht den Unterschied zum Zusammenfügen von Strings mittels "+", "+=" oder Concat().

Die einfachste Erklärung ist vermutlich, dass der Datentyp String unveränderlich ist. Einmal zugewiesen kann der Wert nicht mehr verändert werden. Beim Zusammenfügen von Strings (mittels + oder +=) wird daher ein neuer String erzeugt und die Inhalte der ursprünglichen Zeichenketten werden dorthin kopiert. Das ist generell auch das was im Hintergrund passiert. Man könnte aber auch sagen, dass doch der Compiler hier eine Optimierung durchführen sollte. Das passiert allerdings nicht. Schauen wir uns hierzu mal ein Stück Code an:

private static void ConcatStringsUsingAddition()
{
  string result = String.Empty;

  Stopwatch watch = Stopwatch.StartNew();

  for ( int i = 0; i < 50000; i++ )
  {
    result += "Hallo Welt ";
  }

  watch.Stop();
  Console.WriteLine( "Addition: {0}", watch.Elapsed );
}

Wir fügen hier 50000 Strings zusammen. Das Ergebnis sehen wir hier:

Mehr als 6 Sekunden ist eine recht lange Zeit für eine so simple Operation. Der Code zeigt allerdings auch, warum hier mit Optimierung nicht viel zu machen ist, denn es handelt sich um 50000 Einzeloperationen innerhalb einer Schleife. Versuchen wir es also mal mit der Methode Concat(), die ist ja dafür da Strings miteinander zu verbinden:

private static void ConcatStringsUsingConcat()
{
  string result = String.Empty;

  Stopwatch watch = Stopwatch.StartNew();

  for ( int i = 0; i < 50000; i++ )
  {
    result = String.Concat( result, "Hallo Welt " );
  }

  watch.Stop();
  Console.WriteLine( "Concat: {0}", watch.Elapsed );
}

Das Ergebnis ist:

Concat() ist, je nach Aufrufvariante, in der Tat intern optimiert. Das Problem hier ist wieder, dass es sich um Einzelne Aufrufe handelt, und da ist nicht viel mit Optimieren. Genauer: Die interne Optimierung bringt nichts, weil sie immer nur für einen Aufruf stattfindet, und das 50000 mal. Daher also das erwartet hohe Resultat.

Dass Concat() optimiert ist, können wir an einem anderen Beispiel sehen. Ändern wir den Code mal ab und versuchen wir, eine Liste von Strings (eigentlich ein IEnumerable) mittels Concat() zusammenzufügen. Dafür gibt es auch eine entsprechende Überladung:

private static void ConcatStringListUsingConcat()
{
  Stopwatch watch = Stopwatch.StartNew();

  // Prepare the list of strings
  List<string> stringList = new List<string>();

  for ( int i = 0; i < 50000; i++ )
  {
    stringList.Add( "Hallo Welt " );
  }

  string result = String.Concat( stringList );

  watch.Stop();

  Console.WriteLine( "Concat List: {0}", watch.Elapsed );
}

Das Ergebnis zeigt, dass diese Variante erheblich schneller ist - und das, obwohl im Code (mit Absicht übrigens) das Erzeugen der Liste mit in die Zeitmessung eingeflossen ist.



Tatsächlich arbeitet bei dieser Variante im Hintergrund ein StringBuilder, der die Strings zusammensetzt. Mit ein bisschen Overhead und natürlich zuzüglich der Erstellung der Liste und dem Hinzufügen der Strings.

Zu guter Letzt noch die schnellste Variante:

private static void ConcatStringsUsingStringBuilder()
{
  StringBuilder result = new StringBuilder();

  Stopwatch watch = Stopwatch.StartNew();

  for ( int i = 0; i < 50000; i++ )
  {
    result.Append( "Hallo Welt " );
  }

  watch.Stop();
  Console.WriteLine( "StringBuilder: {0}", watch.Elapsed );
}

Das Ergebnis ist wie folgt:

Ich hoffe dieser kleine Artikel hilft denjenigen ein bisschen weiter, die bisher Probleme hatten, zu verstehen, warum es eine langsame Methode gibt, Strings zusammenzufügen und eine schnelle. Übrigens: Wenn es nur um zwei Strings geht, macht euch bitte nicht die Mühe einen StringBuilder zu erstellen, das ist schneller ohne - das Erzeugen des StringBuilder-Objekts braucht ja auch ein bisschen Zeit.

Kommentare

Es gibt noch keine Kommentare für diesen Artikel

Kommentar hinzufügen

13 + 16 =