it-swarm-eu.dev

Gründe für die Verwendung von StringBuffer in Java anstelle des Stringverkettungsoperators

Jemand sagte mir, es sei effizienter, StringBuffer zu verwenden, um Zeichenfolgen in Java) zu verketten, als das + Operator für Strings. Was passiert dabei unter der Haube? Was macht StringBuffer anders?

54
harry

Es ist heutzutage in fast allen Fällen besser, StringBuilder zu verwenden (es ist eine nicht synchronisierte Version; wann erstellen Sie Strings parallel?), Aber Folgendes passiert:

Wenn Sie + mit zwei Zeichenfolgen verwenden, wird der Code folgendermaßen kompiliert:

String third = first + second;

Auf so etwas:

StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();

Daher macht es für nur kleine Beispiele normalerweise keinen Unterschied. Wenn Sie jedoch eine komplexe Zeichenfolge erstellen, müssen Sie häufig noch viel mehr erledigen. Beispielsweise könnten Sie viele verschiedene angehängte Anweisungen oder eine Schleife wie die folgende verwenden:

for( String str : strings ) {
  out += str;
}

In diesem Fall sind in jeder Iteration eine neue StringBuilder -Instanz und eine neue String (der neue Wert von out - Strings ist unveränderlich) erforderlich. Das ist sehr verschwenderisch. Wenn Sie dies durch ein einzelnes StringBuilder ersetzen, können Sie nur ein einzelnes String erstellen und den Heap nicht mit Strings füllen, die Sie nicht interessieren.

62
Calum

Für einfache Verkettungen wie:

String s = "a" + "b" + "c";

Es ist ziemlich sinnlos, StringBuffer - zu verwenden, da jodonnell darauf hingewiesen hat, dass es intelligent übersetzt wird in:

String s = new StringBuffer().append("a").append("b").append("c").toString();

ABER es ist sehr unperformant, Zeichenfolgen in einer Schleife zu verketten, wie:

String s = "";
for (int i = 0; i < 10; i++) {
    s = s + Integer.toString(i);
}

Wenn Sie in dieser Schleife string verwenden, werden 10 Zwischen-String-Objekte im Speicher generiert: "0", "01", "012" usw. Während Sie dasselbe mit StringBuffer schreiben, aktualisieren Sie einfach einen internen Puffer von StringBuffer und erstellen nicht die Zwischenzeichenfolgenobjekte, die Sie nicht benötigen:

StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}

Tatsächlich sollten Sie für das obige Beispiel StringBuilder (eingeführt in Java 1.5)) anstelle von StringBuffer - StringBuffer verwenden, da alles etwas schwerer ist Methoden werden synchronisiert.

42
tkokoszka

Einer sollte nicht schneller sein als der andere. Dies war vor Java 1.4.2) nicht der Fall, da bei der Verkettung von mehr als zwei Zeichenfolgen mit dem Operator "+" während des Erstellungsprozesses Zwischenobjekte String erstellt werden die letzte Saite.

Wie jedoch in JavaDoc for StringBuffer angegeben ist, werden Java 1.4.2 mit dem Operator "+" kompiliert, um StringBuffer und zu erstellen append() mit den vielen Zeichenketten, also anscheinend kein Unterschied.

Seien Sie jedoch vorsichtig, wenn Sie eine Zeichenfolge zu einer anderen innerhalb einer Schleife hinzufügen! Beispielsweise:

String myString = "";

for (String s : listOfStrings) {
  // Be careful! You're creating one intermediate String object
  // for every iteration on the list (this is costly!)
  myString += s;
}

Beachten Sie jedoch, dass das Verketten einiger Zeichenfolgen mit "+" in der Regel sauberer ist, als wenn Sie alle Zeichenfolgen mit append() verknüpfen.

20
André Chalella

Unter der Haube wird ein StringBuffer erstellt und angehängt, der im Ergebnis toString () aufruft. Es spielt also eigentlich keine Rolle mehr, welche Sie verwenden.

So

String s = "a" + "b" + "c";

wird

String s = new StringBuffer().append("a").append("b").append("c").toString();

Dies gilt für eine Reihe von eingebetteten Anhängen in einer einzigen Anweisung. Wenn Sie Ihre Zeichenfolge über mehrere Anweisungen hinweg erstellen, verschwenden Sie Speicher und ein StringBuffer oder StringBuilder ist die bessere Wahl.

9
jodonnell

Ich denke, dass jdk1.5 (oder höher) und Ihre Verkettung thread-sicher ist. Verwenden Sie StringBuilder anstelle von StringBuffer http://Java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs) -stringbuilder.html Zu den Geschwindigkeitsgewinnen: http://www.about280.com/stringtest.html

Persönlich würde ich Code zur besseren Lesbarkeit verwenden. Wenn Sie also nicht feststellen, dass die Verkettung von Zeichenfolgen Ihren Code erheblich verlangsamt, sollten Sie bei der Methode bleiben, die Ihren Code besser lesbar macht.

7
slipset

In einigen Fällen ist dies aufgrund der vom Compiler vorgenommenen Optimierungen veraltet, aber das allgemeine Problem ist der folgende Code:

string myString="";
for(int i=0;i<x;i++)
{
    myString += "x";
}

wird wie folgt vorgehen (jeder Schritt ist die nächste Schleifeniteration):

  1. konstruiere ein String-Objekt der Länge 1 und des Wertes "x"
  2. Erstellen Sie ein neues String-Objekt der Größe 2, kopieren Sie den alten String "x" hinein und fügen Sie "x" an Position 2 hinzu.
  3. Erstellen Sie ein neues String-Objekt der Größe 3, kopieren Sie den alten String "xx" hinein und fügen Sie "x" an Position 3 hinzu.
  4. ... und so weiter

Wie Sie sehen, muss jede Iteration ein weiteres Zeichen kopieren, was dazu führt, dass wir in jeder Schleife 1 + 2 + 3 + 4 + 5 + ... + N Operationen ausführen. Dies ist eine O (n ^ 2) -Operation. Wenn wir jedoch im Voraus wüssten, dass wir nur N Zeichen benötigen, könnten wir dies in einer einzigen Zuordnung tun, wobei wir nur N Zeichen aus den von uns verwendeten Zeichenfolgen kopieren - ein bloßes O(n) Betrieb.

StringBuffer/StringBuilder vermeiden dies, da sie veränderlich sind und daher nicht immer wieder dieselben Daten kopieren müssen (solange in ihrem internen Puffer Platz zum Kopieren vorhanden ist). Sie vermeiden es, eine Zuordnung und Kopie proportional zur Anzahl der ausgeführten Anhänge vorzunehmen, indem sie ihren Puffer um einen Teil seiner aktuellen Größe überbelegen und amortisiert O(1) anhängen.

Es ist jedoch erwähnenswert, dass der Compiler häufig in der Lage ist, Code automatisch in den StringBuilder-Stil zu optimieren (oder besser - da er eine konstante Faltung usw. durchführen kann).

5
Brian

AFAIK, es hängt von der Version der JVM ab, in Versionen vor 1.5 wurde mit "+" oder "+ =" jedes Mal die gesamte Zeichenfolge kopiert.

Beachten Sie, dass mit + = die neue Kopie der Zeichenfolge zugewiesen wird.

Wie bereits erwähnt, beinhaltet die Verwendung von + in Schleifen das Kopieren.

Wenn zusammengesetzte Zeichenfolgen zur Kompilierungszeit zusammengesetzte Kompilierungszeitkonstanten sind, ist dies der Fall

String foo = "a" + "b" + "c";

Hat ist zusammengestellt zu:

String foo = "abc"; 
3
jb.

Java wandelt string1 + string2 in ein StringBuffer-Konstrukt, append () und toString () um. Das macht Sinn.

In Java 1.4 und früher würde dies jedoch für den Operator each + in der Anweisung separat erfolgen. Dies bedeutete, dass ein + ausgeführt wird b + c würde zu zwei StringBuffer-Konstrukten mit zwei toString () -Aufrufen führen. Wenn Sie eine lange Zeichenfolge von concats hätten, würde dies zu einem echten Durcheinander führen bedeutete, dass Sie dies kontrollieren und es richtig machen konnten.

Java 5.0 und höher scheint es vernünftiger zu machen, daher ist es weniger problematisch und sicherlich weniger ausführlich.

3
Alan Krueger

Weitere Informationen:

StringBuffer ist eine thread-sichere Klasse


public final class StringBuffer extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
     public synchronized StringBuffer append(StringBuffer stringbuffer)
    {
        super.append(stringbuffer);
        return this;
    }
// .. skip ..
}

StringBuilder ist jedoch nicht threadsicher, daher ist die Verwendung von StringBuilder nach Möglichkeit schneller


public final class StringBuilder extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
    public StringBuilder append(String s)
    {
        super.append(s);
        return this;
    }
// .. skip ..
}

1
Eric Yung

StringBuffer ist veränderbar. Der Wert der Zeichenfolge wird zum Objekt same hinzugefügt, ohne dass ein anderes Objekt instanziiert wird. Etwas machen wie:

myString = myString + "XYZ"

erstellt ein neues String-Objekt.

1
Loren Segal

Um zwei Zeichenfolgen mit '+' zu verknüpfen, muss für beide Zeichenfolgen eine neue Zeichenfolge mit Leerzeichen zugewiesen werden. Anschließend werden die Daten aus beiden Zeichenfolgen kopiert. Ein StringBuffer ist für die Verkettung optimiert und weist mehr Speicherplatz zu, als anfangs benötigt wird. Wenn Sie einen neuen String verketten, können die Zeichen in den meisten Fällen einfach an das Ende des vorhandenen String-Puffers kopiert werden.
Bei der Verkettung von zwei Zeichenfolgen hat der Operator '+' wahrscheinlich weniger Overhead. Wenn Sie jedoch mehr Zeichenfolgen verketten, wird der StringBuffer mit weniger Speicherzuweisungen und weniger Datenkopien nach vorne verschoben.

1
Eclipse

Die StringBuffer-Klasse verwaltet ein Zeichenarray für den Inhalt der verketteten Zeichenfolgen, während die + -Methode bei jedem Aufruf eine neue Zeichenfolge erstellt und die beiden Parameter (param1 + param2) anfügt.

Der StringBuffer ist schneller, weil 1. er möglicherweise sein bereits vorhandenes Array verwenden kann, um alle Strings zusammenzufassen/zu speichern. 2. Auch wenn sie nicht in das Array passen, ist es schneller, ein größeres Backing-Array zuzuweisen, als für jede Evokation neue String-Objekte zu generieren.

1
Matt Novinger

Da Strings unveränderlich sind, erstellt jeder Aufruf des Operators + ein neues String-Objekt und kopiert die String-Daten in den neuen String. Da das Kopieren eines Strings in der Länge des Strings zeitlich linear ist, führt eine Folge von N Aufrufen des Operators + zu O (N2) Laufzeit (quadratisch).

Da ein StringBuffer dagegen veränderlich ist, muss er nicht jedes Mal, wenn Sie Append () ausführen, den String kopieren, sodass eine Folge von N Append () -Aufrufen O(N) time ( Dies macht nur dann einen signifikanten Unterschied in der Laufzeit, wenn Sie eine große Anzahl von Strings zusammenfügen.

1
Adam Rosenfield

Wie bereits erwähnt, ist das String-Objekt ummutierbar, dh es kann nach seiner Erstellung (siehe unten) nicht mehr geändert werden.

String x = neuer String ("irgendetwas"); // oder

String x = "irgendwas";

Wenn Sie also versuchen, String-Objekte zusammenzufassen, wird der Wert dieser Objekte übernommen und in ein neues String-Objekt eingefügt.

Wenn Sie stattdessen den StringBuffer verwenden, der IS veränderbar ist, fügen Sie die Werte fortlaufend zu einer internen Liste von Zeichen (Primitiven) hinzu, die erweitert oder abgeschnitten werden können, um den erforderlichen Wert zu erreichen. Keine neuen Objekte erstellt werden, werden nur neue Zeichen erstellt/entfernt, wenn dies erforderlich ist, um die Werte zu halten.

1
Christian P.

Wenn Sie zwei Zeichenfolgen verketten, erstellen Sie tatsächlich ein drittes Zeichenfolgenobjekt in Java. Die Verwendung von StringBuffer (oder StringBuilder in Java 5/6)) ist schneller, da ein internes Array von Zeichen zum Speichern der Zeichenfolge verwendet wird und wenn Sie eine der Methoden add (...) verwenden erstellt kein neues String-Objekt, sondern hängt das interne Array an StringBuffer/Buider an.

Bei einfachen Verkettungen spielt es keine Rolle, ob Sie Zeichenfolgen mit StringBuffer/Builder oder dem Operator '+' verketten. Wenn Sie jedoch viele Zeichenfolgen verketten, werden Sie feststellen, dass die Verwendung eines StringBuffer/Builder wesentlich schneller ist.

1

Da Strings in Java unveränderlich sind, wird jedes Mal, wenn Sie einen String verknüpfen, ein neues Objekt im Speicher erstellt. StringBuffer verwendet dasselbe Objekt im Speicher.

0
Ivan Bosnic

Ich denke die einfachste Antwort ist: Es ist schneller.

Wenn Sie wirklich alles unter der Haube wissen wollen, können Sie immer selbst einen Blick auf die Quelle werfen:

http://www.Sun.com/software/opensource/Java/getinvolved.jsp

http://download.Java.net/jdk6/latest/archive/

0
rgcb

Der Abschnitt String Concatenation Operator + der Java Language Specification] gibt Ihnen weitere Hintergrundinformationen darüber, warum der Operator + so langsam sein kann.

0