12 Stimmen

Wie macht Java die String-Verkettung mit "+"?

Ich habe über die Funktionsweise von Java mit += Operator, mit StringBuilder .
Ist es dasselbe mit einer ("a" + "b") Betrieb?

1 Stimmen

Ich empfehle Ihnen, einen Blick auf diese Seite zu werfen hervorragender Artikel .

39voto

T.J. Crowder Punkte 948310

Wenn Sie Folgendes kombinieren wörtlich Strings (wörtlich "foo" + "bar" ), macht der Compiler dies zur Kompilierzeit, nicht zur Laufzeit.

Wenn Sie zwei nicht-literalische Zeichenketten haben und sie mit + verwendet der Compiler (jedenfalls der von Sun) eine StringBuilder unter der Decke, aber nicht unbedingt auf die effizienteste Weise. Wenn Sie also zum Beispiel dies haben:

String repeat(String a, int count) {
    String rv;

    if (count <= 0) {
        return "";
    }

    rv = a;
    while (--count > 0) {
        rv += a;
    }
    return rv;
}

...was der Sun-Compiler tatsächlich als Bytecode erzeugt, sieht so aus etwas wie diese:

String repeat(String a, int count) {
    String rv;

    if (count <= 0) {
        return "";
    }

    rv = a;
    while (--count > 0) {
        rv = new StringBuilder().append(rv).append(a).toString();
    }
    return rv;
}

(Ja, wirklich - siehe die Disassemblierung am Ende dieser Antwort.) Beachten Sie, dass es eine neue StringBuilder bei jeder Iteration, und konvertierte das Ergebnis dann in String . Das ist ineffizient (aber das macht nichts, wenn man es nicht mit einem Los ) wegen der vielen temporären Speicherzuweisungen: Es wird ein StringBuilder und dessen Puffer, ordnet den Puffer möglicherweise beim ersten Mal neu zu append [wenn rv mehr als 16 Zeichen lang ist, was die Standardpuffergröße ist] und wenn nicht auf der ersten, dann fast sicher auf der zweiten append und weist dann eine String am Ende - und tut es dann alle wieder bei der nächsten Iteration.

Sie könnten die Effizienz steigern, indem Sie sie gegebenenfalls so umschreiben, dass sie explizit eine StringBuilder :

String repeat(String a, int count) {
    StringBuilder rv;

    if (count <= 0) {
        return "";
    }

    rv = new StringBuilder(a.length() * count);
    while (count-- > 0) {
        rv.append(a);
    }
    return rv.toString();
}

Dort haben wir eine explizite StringBuilder und auch die anfängliche Pufferkapazität so einstellen, dass sie groß genug ist, um das Ergebnis zu speichern. Das ist speichereffizienter, aber natürlich für unerfahrene Codeverwalter etwas unübersichtlicher und etwas mühsamer zu schreiben. Also wenn Sie ein Leistungsproblem mit einer engen String-Concat-Schleife feststellen, könnte dies eine Möglichkeit sein, dieses Problem zu beheben.

Sie können dies unter der Abdeckung sehen StringBuilder in Aktion mit der folgenden Testklasse:

public class SBTest
{
    public static final void main(String[] params)
    {
        System.out.println(new SBTest().repeat("testing ", 4));
        System.exit(0);
    }

    String repeat(String a, int count) {
        String rv;

        if (count <= 0) {
            return "";
        }

        rv = a;
        while (--count > 0) {
            rv += a;
        }
        return rv;
    }
}

...der (mit Hilfe von javap -c SBTest ) wie folgt:

Compiled from "SBTest.java"
public class SBTest extends java.lang.Object{
public SBTest();
Code:
   0: aload_0
   1: invokespecial  #1; //Method java/lang/Object."<init>":()V
   4: return

public static final void main(java.lang.String[]);
Code:
   0: getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3: new   #3; //class SBTest
   6: dup
   7: invokespecial  #4; //Method "<init>":()V
   10: ldc   #5; //String testing
   12: iconst_4
   13: invokevirtual  #6; //Method repeat:(Ljava/lang/String;I)Ljava/lang/String;
   16: invokevirtual  #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   19: iconst_0
   20: invokestatic   #8; //Method java/lang/System.exit:(I)V
   23: return

java.lang.String repeat(java.lang.String, int);
Code:
   0: iload_2
   1: ifgt  7
   4: ldc   #9; //String
   6: areturn
   7: aload_1
   8: astore_3
   9: iinc  2, -1
   12: iload_2
   13: ifle  38
   16: new   #10; //class java/lang/StringBuilder
   19: dup
   20: invokespecial  #11; //Method java/lang/StringBuilder."<init>":()V
   23: aload_3
   24: invokevirtual  #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   27: aload_1
   28: invokevirtual  #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   31: invokevirtual  #13; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   34: astore_3
   35: goto  9
   38: aload_3
   39: areturn

}

Beachten Sie, wie eine neue StringBuilder wird erstellt am jede Iteration der Schleife und unter Verwendung der Standardpufferkapazität erstellt.

All diese temporären Zuweisungen klingen hässlich, aber auch das nur, wenn man es mit umfangreichen Schleifen und/oder umfangreichen Strings zu tun hat. Wenn der resultierende Bytecode ausgeführt wird, kann die JVM ihn außerdem weiter optimieren. Die HotSpot JVM von Sun beispielsweise ist ein sehr ausgereifter JIT-optimierender Compiler. Sobald er die Schleife als Hot Spot identifiziert hat, kann er durchaus einen Weg finden, sie zu refaktorisieren. Oder natürlich auch nicht :-)

Meine Faustregel ist, dass ich mich darum kümmere, wenn ich ein Leistungsproblem sehe, oder wenn ich weiß, dass ich eine Los der Verkettung und es ist sehr wahrscheinlich ein Leistungsproblem zu sein, und der Code wird vom Standpunkt der Wartungsfreundlichkeit nicht wesentlich beeinträchtigt, wenn ich eine StringBuilder stattdessen. Die wütende Liga der Optimierungsgegner würde mir bei der zweiten Frage wahrscheinlich widersprechen :-)

0 Stimmen

@Tom Brito: Eigentlich bin ich mir aufgrund der Frage nicht sicher, ob die Antwort mit einem Wort "ja" oder "nein" lauten würde, also habe ich das herausgenommen und nur erklärt, was vor sich geht :-)

1 Stimmen

@T.J.Crowder du rockst!!(Ich weiß, es ist kein empfohlener Kommentar... ich konnte nicht widerstehen :P)

14voto

Pablo Santa Cruz Punkte 169147

Nein. Es ist nicht dasselbe, wenn man StringBuilder als zu tun "a" + "b" .

In Java sind String-Instanzen unveränderlich.

Also, wenn Sie es tun:

String c = "a" + "b";

Bei jeder Verkettung erstellen Sie neue Strings.

Andererseits ist der StringBuilder wie ein Puffer, der beim Anhängen neuer Strings nach Bedarf wachsen kann.

StringBuilder c = new StringBuilder();
c.append("a");
c.append("b"); // c is only created once and appended "a" and "b".

Als Faustregel gilt (dank der Kommentare, die ich erhalten habe, geändert):

Wenn Sie viel konkatenieren wollen (z.B. innerhalb einer Schleife konkatenieren oder eine große XML-Datei erzeugen, die aus mehreren konkatenierten Variablen besteht), sollten Sie StringBuilder verwenden. Ansonsten ist eine einfache Verkettung (mit dem Operator +) völlig ausreichend.

Auch Compiler-Optimierungen spielen bei der Kompilierung dieser Art von Code eine große Rolle.

Hier ist weitere Erläuterungen zu diesem Thema.

Und weitere StackOVerflow-Fragen zu diesem Thema:

Ist es besser, einen StringBuilder in einer Schleife wiederzuverwenden?

Wie kann man in Java am besten eine Zeichenkette mit abgegrenzten Elementen erstellen?

StringBuilder vs. String-Verkettung in toString() in Java

6 Stimmen

Es ist nicht garantiert, dass Sie jedes Mal neue Strings erstellen, wenn Sie verketten; es liegt tatsächlich am Compiler. Ein minderwertiger Compiler würde sich so verhalten, wie Sie es beschreiben...

1 Stimmen

Nicht unbedingt. Der Compiler wird es sowieso optimieren. Ändern Sie Ihre Faustregel in: "Wenn Sie Folgendes verwenden wollen stringA += stringB verwenden, dann StringBuilder stattdessen, weil es in der Tat Haufen essen würde. Ihre Antwort impliziert eher, dass Sie eine StringBuilder für jeden stringA + stringB ist dies nicht der Fall.

5 Stimmen

Tatsächlich verlangt die JLS, dass die Verkettung von Kompilierzeitkonstanten zu einem internierten String . Als Faustregel sollte gelten, dass Sie bei der Erstellung eines String in einer Schleife und es besteht die Möglichkeit, dass die Schleife mehrmals durchlaufen wird, ist die Verwendung eines StringBuilder . Es macht keinen Sinn, den linearen Verkettungscode mit StirngBuilder .

6voto

Donal Fellows Punkte 125686

Ja, das ist dasselbe, aber der Compiler kann zusätzlich die Verkettung von Literale bevor Sie den Code ausgeben, also "a"+"b" kann einfach ausgestellt werden als "ab" direkt.

0 Stimmen

Nein, sie sind nicht das gleiche; wie können sie sein - Verkettung mit unveränderlichen Strings & Verkettung mit String Builer ? @Pablo Santa Cruz liefert eine würdige Antwort.

0 Stimmen

@phoenix24: Eigentlich gibt er nur dummes Zeug von sich. genau weil er nicht zwischen wörtlicher Verkettung und nicht-wörtlicher Verkettung unterscheidet.

4voto

Christian Semrau Punkte 8588

Für die Verkettung einer festen Anzahl von Zeichenketten in einem Ausdruck con + erzeugt der Compiler einen Code, der eine einzige StringBuilder .

Z.B. die Zeile

String d = a + b + c;

führt zu demselben Bytecode wie die Zeile

String d = new StringBuilder().append(a).append(b).append(c).toString();

wenn sie mit dem javac-Compiler kompiliert werden. (Der Eclipse-Compiler erzeugt etwas optimierteren Code durch den Aufruf von new StringBuilder(a) und spart so einen Methodenaufruf).

Wie bereits in anderen Antworten erwähnt, verkettet der Compiler String-Literale wie "a" + "b" in eine Zeichenkette selbst, wodurch Bytecode entsteht, der "ab" stattdessen.

Wie überall im Netz erwähnt, sollten Sie keine + eine Zeichenkette zu erstellen innerhalb einer Schleife da Sie den Anfang der Zeichenkette immer wieder in neue Zeichenketten kopieren. In dieser Situation sollten Sie eine StringBuilder die Sie außerhalb der Schleife deklarieren.

0voto

ring bearer Punkte 19605

"a" + "b" Betrieb

Die Verkettung von Zeichenketten mit "+" ist zwar lesbar, leicht zu formatieren und einfach zu handhaben, gilt aber in Java als schlecht.

Jedes Mal, wenn Sie etwas mittels '+' (String.concat()) anhängen, wird ein neuer String erstellt, der Inhalt des alten Strings kopiert, der neue Inhalt angehängt und der alte String verworfen. Je größer der String wird, desto länger dauert es - es muss mehr kopiert werden und es wird mehr Müll produziert. Anmerkung: Wenn Sie nur ein paar (sagen wir 3,4) Zeichenfolgen verketten und keine Zeichenfolge über eine Schleife aufbauen oder nur eine Testanwendung schreiben, können Sie immer noch mit "+" arbeiten.

Verwendung von StringBuilder

Bei umfangreichen String-Manipulationen (oder beim Anhängen in einer Schleife) ist das Ersetzen von "+" durch StringBuilder .append wird wahrscheinlich empfohlen. Die im Falle von "+" erwähnten Zwischenobjekte werden nicht während append() Methodenaufruf.

Zu beachten ist auch, dass die Optimierungen im Sun Java Compiler, der automatisch die StringBuilders ( StringBuffers < 5.0), wenn er String-Verkettungen sieht. Aber das ist nur der Sun Java Compiler.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X