Frage

Angenommen, String a und b:

a += b
a = a.concat(b)

Sind sie unter der Haube dasselbe?

Hier wird Concat als Referenz dekompiliert.Ich würde gerne in der Lage sein, das zu dekompilieren + auch den Operator, um zu sehen, was das bewirkt.

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}
War es hilfreich?

Lösung

Nein, nicht ganz.

Erstens gibt es einen kleinen Unterschied in der Semantik.Wenn a Ist null, Dann a.concat(b) wirft ein NullPointerException Aber a+=b behandelt den ursprünglichen Wert von a Als wäre es null.Darüber hinaus ist die concat() Methode akzeptiert nur String Werte während die + Der Operator wandelt das Argument stillschweigend in einen String um (unter Verwendung von toString() Methode für Objekte).Also die concat() Die Methode ist strenger in dem, was sie akzeptiert.

Um einen Blick hinter die Kulissen zu werfen, schreiben Sie eine einfache Klasse mit a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

Jetzt zerlegen mit javap -c (im Sun JDK enthalten).Sie sollten eine Auflistung sehen, die Folgendes enthält:

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

Also, a += b ist das Äquivalent von

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

Der concat Methode sollte schneller sein.Allerdings mit mehr Saiten StringBuilder Methode gewinnt, zumindest was die Leistung betrifft.

Der Quellcode von String Und StringBuilder (und seine package-private Basisklasse) ist in src.zip des Sun JDK verfügbar.Sie können sehen, dass Sie ein char-Array aufbauen (bei Bedarf die Größe ändern) und es dann wegwerfen, wenn Sie das endgültige erstellen String.In der Praxis erfolgt die Speicherzuweisung überraschend schnell.

Aktualisieren: Wie Pawel Adamski feststellt, hat sich die Leistung im neueren HotSpot verändert. javac erzeugt immer noch genau den gleichen Code, aber der Bytecode-Compiler betrügt.Einfache Tests schlagen völlig fehl, weil der gesamte Code weggeworfen wird.Summieren System.identityHashCode (nicht String.hashCode) zeigt die StringBuffer Code hat einen leichten Vorteil.Änderungen vorbehalten, wenn das nächste Update veröffentlicht wird oder wenn Sie eine andere JVM verwenden.Aus @lukaseder, eine Liste der HotSpot-JVM-Intrinsics.

Andere Tipps

Niyaz ist richtig, aber es ist auch erwähnenswert, dass der spezielle +-Operator vom Java-Compiler in etwas Effizienteres umgewandelt werden kann.Java verfügt über eine StringBuilder-Klasse, die einen nicht threadsicheren, veränderlichen String darstellt.Bei der Durchführung einer Reihe von String-Verkettungen führt der Java-Compiler eine stille Konvertierung durch

String a = b + c + d;

hinein

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

was für große Saiten deutlich effizienter ist.Soweit ich weiß, passiert dies nicht, wenn Sie die Concat-Methode verwenden.

Die Concat-Methode ist jedoch effizienter, wenn ein leerer String mit einem vorhandenen String verkettet wird.In diesem Fall muss die JVM kein neues String-Objekt erstellen und kann einfach das vorhandene zurückgeben.Sehen die Concat-Dokumentation um dies zu bestätigen.

Wenn Sie also großen Wert auf Effizienz legen, sollten Sie beim Verketten möglicherweise leerer Zeichenfolgen die Methode concat verwenden und andernfalls + verwenden.Der Leistungsunterschied sollte jedoch vernachlässigbar sein und Sie sollten sich darüber wahrscheinlich nie Sorgen machen.

Ich habe einen ähnlichen Test wie @marcio durchgeführt, aber stattdessen mit der folgenden Schleife:

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

Nur zur Sicherheit habe ich noch eingeworfen StringBuilder.append() sowie.Jeder Test wurde 10 Mal durchgeführt, mit 100.000 Wiederholungen pro Durchgang.Hier sind die Ergebnisse:

  • StringBuilder gewinnt zweifellos.Das Uhrzeitergebnis war für die meisten Läufe 0, und der längste dauerte 16 ms.
  • a += b dauert etwa 40000 ms (40 s) für jeden Durchlauf.
  • concat erfordert nur 10000 ms (10 s) pro Lauf.

Ich habe die Klasse noch nicht dekompiliert, um die Interna anzuzeigen oder sie über den Profiler auszuführen, aber ich vermute es a += b verbringt viel Zeit damit, neue Objekte zu erstellen StringBuilder und sie dann wieder in konvertieren String.

Die meisten Antworten hier stammen aus dem Jahr 2008.Es sieht so aus, als hätten sich die Dinge im Laufe der Zeit verändert.Meine neuesten Benchmarks mit JMH zeigen das auf Java 8 + ist etwa doppelt so schnell wie concat.

Mein Maßstab:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}

Ergebnisse:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s

Tom hat Recht, wenn er genau beschreibt, was der +-Operator tut.Es entsteht ein Provisorium StringBuilder, hängt die Teile an und endet mit toString().

Allerdings ignorieren alle bisherigen Antworten die Auswirkungen von HotSpot-Laufzeitoptimierungen.Insbesondere werden diese temporären Vorgänge als allgemeines Muster erkannt und zur Laufzeit durch effizienteren Maschinencode ersetzt.

@marcio:Sie haben eine erstellt Mikro-Benchmark;Bei modernen JVMs ist dies keine gültige Methode zum Profilieren von Code.

Der Grund, warum die Laufzeitoptimierung wichtig ist, liegt darin, dass viele dieser Unterschiede im Code – sogar einschließlich der Objekterstellung – völlig anders sind, sobald HotSpot in Betrieb geht.Die einzige Möglichkeit, dies sicher zu wissen, ist die Profilierung Ihres Codes vor Ort.

Schließlich sind alle diese Methoden tatsächlich unglaublich schnell.Dies könnte ein Fall einer vorzeitigen Optimierung sein.Wenn Sie Code haben, der häufig Zeichenfolgen verkettet, hängt der Weg zur maximalen Geschwindigkeit wahrscheinlich nicht davon ab, welche Operatoren Sie wählen, sondern vom Algorithmus, den Sie verwenden!

Wie wäre es mit einem einfachen Test?Habe den folgenden Code verwendet:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • Der "a + b" Version ausgeführt in 2500 ms.
  • Der a.concat(b) hingerichtet 1200ms.

Mehrmals getestet.Der concat() Die Versionsausführung dauerte im Durchschnitt halb so lange.

Dieses Ergebnis hat mich überrascht, weil das concat() Die Methode erstellt immer eine neue Zeichenfolge (sie gibt ein „ zurück).new String(result)".Es ist bekannt, dass:

String a = new String("a") // more than 20 times slower than String a = "a"

Warum war der Compiler nicht in der Lage, die String-Erstellung im „a + b“-Code zu optimieren, obwohl er wusste, dass dies immer zu demselben String führte?Dadurch könnte die Erstellung einer neuen Zeichenfolge vermieden werden.Wenn Sie die obige Aussage nicht glauben, testen Sie es selbst.

Grundsätzlich gibt es zwei wichtige Unterschiede zwischen + und the concat Methode.

  1. Wenn Sie das verwenden Konkat Methode dann könnten Sie Strings nur verketten, während im Fall der + Mit dem Operator können Sie die Zeichenfolge auch mit einem beliebigen Datentyp verketten.

    Zum Beispiel:

    String s = 10 + "Hello";
    

    In diesem Fall sollte die Ausgabe sein 10Hallo.

    String s = "I";
    String s1 = s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    Im oben genannten Fall müssen Sie zwingend zwei Zeichenfolgen angeben.

  2. Der zweite und wichtigste Unterschied zwischen + Und Konkat ist das:

    Fall 1:Angenommen, ich verkette die gleichen Zeichenfolgen mit Konkat Betreiber auf diese Weise

    String s="I";
    String s1=s.concat("am").concat("good").concat("boy");
    System.out.println(s1);
    

    In diesem Fall beträgt die Gesamtzahl der im Pool erstellten Objekte 7 wie folgt:

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy
    

    Fall 2:

    Jetzt werde ich die gleichen Zeichenfolgen über verketten + Operator

    String s="I"+"am"+"good"+"boy";
    System.out.println(s);
    

    Im obigen Fall beträgt die Gesamtzahl der erstellten Objekte nur 5.

    Eigentlich, wenn wir die Strings über verketten + Operator verwaltet dann eine StringBuffer-Klasse, um dieselbe Aufgabe wie folgt auszuführen:

    StringBuffer sb = new StringBuffer("I");
    sb.append("am");
    sb.append("good");
    sb.append("boy");
    System.out.println(sb);
    

    Auf diese Weise werden nur fünf Objekte erstellt.

Also Leute, das sind die grundlegenden Unterschiede zwischen + und das Konkat Methode.Genießen :)

Der Vollständigkeit halber wollte ich hinzufügen, dass die Definition des „+“-Operators im zu finden ist JLS SE8 15.18.1:

Wenn nur ein Operandenausdruck vom Typ String ist, wird string Konvertierung (§5.1.11) wird auf dem anderen Operanden durchgeführt, um eine Zeichenfolge zur Laufzeit.

Das Ergebnis der Zeichenfolgenverkettung ist ein Verweis auf ein String-Objekt Das ist die Verkettung der beiden Operandenzeichenfolgen.Die Charaktere des linken Operanden vor den Zeichen des rechten Operanden Operand in der neu erstellten Zeichenfolge.

Das String-Objekt wird neu erstellt (§12.5), es sei denn, der Ausdruck ist ein konstanter Ausdruck (§15.28).

Zur Umsetzung sagt das JLS Folgendes:

Eine Implementierung kann sich für die Durchführung von Konvertierung und Verkettung entscheiden in einem Schritt, um zu vermeiden, dass ein Zwischenprodukt erstellt und dann verworfen wird String-Objekt.So erhöhen Sie die Leistung von wiederholten Zeichenfolgen Verkettung kann ein Java-Compiler die StringBuffer-Klasse oder eine Ähnliches Verfahren zum Reduzieren der Anzahl von String-Zwischenobjekten , die durch die Auswertung eines Ausdrucks erstellt werden.

Bei primitiven Typen kann eine Implementierung auch die Erstellung eines Wrapper-Objekts durch direkte Konvertierung aus einem primitiven in eine Zeichenfolge ein.

Wenn man also davon ausgeht, dass ein Java-Compiler möglicherweise die StringBuffer-Klasse oder eine ähnliche Technik zum Reduzieren verwendet, könnten verschiedene Compiler unterschiedliche Bytecodes erzeugen.

Der + Betreiber kann zwischen einem String und einem String-, Char-, Integer-, Double- oder Float-Datentypwert arbeiten.Der Wert wird vor der Verkettung lediglich in seine Zeichenfolgendarstellung konvertiert.

Der Concat-Operator kann nur an und mit Saiten durchgeführt werden.Es prüft die Datentypkompatibilität und gibt einen Fehler aus, wenn sie nicht übereinstimmen.

Abgesehen davon macht der von Ihnen bereitgestellte Code dasselbe.

Das glaube ich nicht.

a.concat(b) ist in String implementiert und ich denke, dass sich an der Implementierung seit den frühen Java-Maschinen nicht viel geändert hat.Der + Die Operationsimplementierung hängt von der Java-Version und dem Compiler ab.Momentan + wird mit implementiert StringBuffer um die Operation so schnell wie möglich zu gestalten.Vielleicht wird sich das in Zukunft ändern.In früheren Java-Versionen + Die Operation an Strings war viel langsamer, da sie Zwischenergebnisse lieferte.

Ich vermute, dass += wird mit implementiert + und entsprechend optimiert.

Bei Verwendung von + nimmt die Geschwindigkeit mit zunehmender Länge des Strings ab, bei Verwendung von concat ist die Geschwindigkeit jedoch stabiler, und die beste Option ist die Verwendung der Klasse StringBuilder, die über eine stabile Geschwindigkeit verfügt, um dies zu erreichen.

Ich denke, Sie können verstehen, warum.Der absolut beste Weg zum Erstellen langer Zeichenfolgen ist jedoch die Verwendung von StringBuilder() und append(). Beide Geschwindigkeiten sind inakzeptabel.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top