Concatenarea șirurilor: concat() vs "+" operator

Presupunând că Șirul a și b:

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

Sub capota, sunt același lucru?

Aici este concat decompilat ca referință. Am'd place să fie în măsură să separe " + " de operator, precum și pentru a vedea ce face.

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);
    }
}
Comentarii la întrebare (4)
Soluția

Nu, nu chiar.

În primul rând, nu's o ușoară diferență în semantica. Dacă " a "este nul", apoi " un.concat(b) aruncă o NullPointerException " dar "o+=b va trata valoarea inițială a" a " ca și cum ar fi null. Pe de altă parte, concat()metoda acceptă numai "String" valori în timp ce pe " + " operator tăcere converti argument un Șir de caractere (folosindtoString () metoda pentru obiecte). Deci concat() metoda este mult mai strictă în ceea ce-l acceptă.

Să se uite sub capota, scrie o clasă simplă cu a += b;

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

Acum demontați cu javap -c (inclus în Sun JDK). Ar trebui să vedeți o listă, inclusiv:

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."":()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

Deci, o += b este echivalentul a

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

Anii concat metodă ar trebui să fie mai rapid. Cu toate acestea, cu mai multe siruri de caractere de la StringBuilder metoda victorii, cel puțin în termeni de performanță.

Codul sursă al String si StringBuilder (și ambalajul său-private în clasa de bază) este disponibil în src.zip de Soare JDK. Puteți vedea că sunteți construirea o matrice char (redimensionare după cum este necesar) și apoi a aruncat-o departe, atunci când vă creați final String. În practica de alocare de memorie este surprinzător de repede.

Actualizare: Ca Pawel Adamski note, performanta s-a schimbat în ultimii HotSpot. javac încă produce exact același cod, dar bytecode compiler ieftin. Simplu de testare în întregime eșuează pentru că întregul corp de cod este aruncat. Însumarea Sistem.identityHashCode (nu Șir de caractere.hashCode) arată StringBuffer cod are un ușor avantaj. Supuse de a modifica atunci când următoarea actualizare este lansat, sau dacă utilizați un alt JVM. Din @lukaseder, o listă de HotSpot JVM intrinsics.

Comentarii (11)

Niyaz este corect, dar's, de asemenea, demn de remarcat faptul că în special operatorul + poate fi transformat în ceva mult mai eficient de către compilatorul Java. Java are un StringBuilder clasă care reprezintă un non-thread-safe, mutabil Șir. Atunci când se efectuează o grămadă de Șir înlănțuirilor, Java compiler în tăcere convertește

String a = b + c + d;

în

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

care pentru siruri de caractere mari este semnificativ mai eficient. Din câte știu, acest lucru nu se întâmplă atunci când utilizați metoda concat.

Cu toate acestea, metoda concat este mult mai eficient atunci când este concatenarea unui Șir gol pe un Șir existente. În acest caz, JVM nu are nevoie pentru a crea un nou obiect String și puteți întoarce pur și simplu cel existent. A se vedea la concat documentația pentru a confirma acest lucru.

Deci, dacă te're super-preocupat de randament atunci ar trebui să utilizați metoda concat când concatenarea eventual-Siruri de caractere goale, și de a folosi + în caz contrar. Cu toate acestea, diferenta de performanta ar trebui să fie neglijabil și nu ar trebui't vă faceți griji vreodată despre asta.

Comentarii (3)

Am dat un test similar ca @marcio, dar cu următoarea buclă în loc:

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
}

Doar pentru o bună măsură, m-am aruncat în `StringBuilder.append (), la fel de bine. Fiecare test a fost rulat de 10 ori, cu 100 de mii de repetari pentru fiecare serie. Aici sunt rezultatele:

  • StringBuilder pune mâinile în jos. Ceas de timp rezultatul a fost 0 pentru cel mai execută, iar cele mai lungi luat 16ms.
  • o += b ia despre 40000ms (40 de ani) pentru fiecare termen.
  • concat necesită doar 10000ms (10s) pe fugă.

Nu am't decompilat clasa pentru a vedea interne sau rulați-l prin profiler încă, dar bănuiesc că a += b petrece mare parte din timp, crearea de noi obiecte de StringBuilderși apoi de a le converti înapoi laString`.

Comentarii (3)

Cele mai multe răspunsuri aici sunt din 2008. Se pare că lucrurile s-au schimbat de-a lungul timpului. Ultimul meu referință realizate cu JMH arată că pe Java 8 + este de aproximativ două ori mai repede decât concat.

Meu de referință:

@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))));
    }
}

Rezultate:

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
Comentarii (1)

Tom este corect în a descrie exact ceea ce operatorul + are. Se creează temporar un StringBuilder, adaugă părți, și se termină cu toString().

Cu toate acestea, toate răspunsurile de până acum sunt ignorând efectele HotSpot runtime optimizări. În mod specific, aceste temporar operațiunile sunt recunoscute ca un model comun și sunt înlocuite cu altele mai eficiente cod mașină la run-time.

@marcio: Te'am creat o micro-referință; cu moderne JVM's nu este un mod valabil de la profil cod.

Motivul run-time de optimizare contează este că multe dintre aceste diferențe în cod-chiar inclusiv-obiect crearea -- sunt complet diferite odată HotSpot se întâmplă. Singura modalitate de a ști sigur este profilare codul in situ.

În cele din urmă, toate aceste metode sunt, de fapt, incredibil de rapid. Acest lucru ar putea fi un caz de nastere prematura de optimizare. Dacă aveți un cod care concatenează șiruri de un lot, mod de a obține viteza maximă, probabil, nu are nimic de-a face cu care operatorii le alegeți și în loc algoritmul tu're folosind!

Comentarii (1)

Ce zici de niște simple de testare? Folosit codul de mai jos:

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);
  • La `"a + b" versiune, executat în 2500ms.
  • A `a.concat(b) executat în 1200ms.

Testat de mai multe ori. Anii concat() versiune de execuție a luat jumătate din timp, în medie.

Acest rezultat m-a surprins pentru că concat() metoda creează întotdeauna o nouă string (returnează un " nou String(rezultat)`". L's bine cunoscut faptul că:

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

De ce a fost't compilator capabil de a optimiza șir de creație în "a + b" codul, știind că a condus întotdeauna la același șir? S-ar putea evita un nou șir de creație. Dacă tu nu't cred că afirmația de mai sus, testul pentru sine dumneavoastră.

Comentarii (0)

Practic, există două diferențe importante între + și concat metoda.

  1. Dacă utilizați concat metoda atunci va fi doar posibilitatea de a concatena șiruri în timp ce în cazul în care + operatorul, de asemenea, puteți înlănțui șir cu orice tip de date.

De Exemplu:

String s = 10 + "Buna ziua";

În acest caz, rezultatul ar trebui să fie 10Hello.

String s = "I"; String s1 = s.concat("sunt").concat("bun").concat("baiat"); Sistem.afară.println(s1);

În cazul de mai sus, trebuie să furnizeze două șiruri obligatorie.

  1. Cea de-a doua și principala diferență între + și concat este asta:

Cazul 1: Să presupunem că am concat aceleași sfori cu concat operatorul în acest fel

String s="I"; String s1=s.concat("sunt").concat("bun").concat("baiat"); Sistem.afară.println(s1);

În acest caz numărul total de obiecte create în piscină sunt 7 astfel:

Am sunt buna băiatul Iam Iamgood Iamgoodboy

Cazul 2:

Acum am de gând să concatinate același siruri de via + operatorul

String s="I"+"sunt"+"bun"+"baiat"; Sistem.afară.println(s);

În cazul de mai sus, numărul total de obiecte create sunt doar 5.

De fapt, atunci când ne-am concatinate siruri de caractere via + operatorul de atunci se menține o clasa StringBuffer pentru a efectua aceeași sarcină, după cum urmează:-

StringBuffer sb = new StringBuffer("I"); sb.append("sunt"); sb.append("bun"); sb.append("baiat"); Sistem.afară.println(sb);

În acest fel se va crea doar cinci obiecte.

Deci tipii ăștia sunt diferențele de bază între + și concat metoda. Bucurați-vă de :)

Comentarii (3)

Pentru motive de exhaustivitate, am vrut să adaug că definiția '+' operator pot fi găsite în JLS SE8 15.18.1:

Dacă numai un singur operand expresia este de tip String, apoi string conversie (§5.1.11) se face pe celălalt operand pentru a produce un string la timp a alerga.

rezultatul de concatenare șir este o referință la un obiect String care este concatenarea a doi operanzi siruri de caractere. Personajele de la stânga operand precede caracterele de la dreapta operand în nou creat șir.

String obiect nou creat (§12.5) dacă expresia este un expresie constantă (§15.28).

Despre punerea în aplicare JLS spune următoarele:

O punere în aplicare poate alege pentru a efectua conversia și concatenare într-un singur pas, pentru a evita crearea și apoi aruncarea intermediar obiect String. Pentru a crește performanța repetate string concatenare, un compilatorul Java poate folosi clasa StringBuffer sau un tehnică similară pentru a reduce numărul de intermediari Șir de obiecte care sunt create de evaluare a unei expresii.

Pentru tipurile primitive, o implementare poate optimiza, de asemenea, departe crearea unui obiect înveliș de conversie direct de la un primitiv tip la un șir de caractere.

Deci, judecând din 'un compilatorul Java poate folosi clasa StringBuffer sau o tehnică similară pentru a reduce', diferite compilatoare ar putea produce diferite byte-code.

Comentarii (0)

La operatorul + ** poate lucra între un șir de caractere și un string, char, integer, double sau float date de tip valoare. Doar convertește valoarea acestuia reprezentare șir înainte de concatenare.

La concat operator poate fi făcut și cu siruri de caractere. Se verifică pentru tipul de date de compatibilitate și aruncă o eroare, dacă ei nu't de meci.

Cu excepția acest lucru, codul furnizat face aceleasi lucruri.

Comentarii (0)

Eu nu't cred.

o.concat(b) este implementat în Șir și cred că punerea în aplicare n&#39;t se schimba prea mult de la începutul java masini. Butonul " + " operațiunea de punere în aplicare depinde de versiune Java și compilator. În prezent, " + " este implementat folosind [StringBuffer][1] pentru a face operația cât mai repede posibil. Poate că în viitor, acest lucru se va schimba. În versiunile mai vechi de java+` operațiune pe Siruri de caractere a fost mult mai lent ca a produs rezultate intermediare.

Cred că += este implementat folosind " + " și în mod similar optimizat.

Comentarii (2)

Atunci când se utilizează +, viteza scade ca string's lungimea crește, dar atunci când se utilizează concat, viteza este mult mai stabil, și cea mai bună opțiune este utilizarea StringBuilder clasa care are viteză stabilă, în scopul de a face asta.

Cred că puteți înțelege de ce. Dar cel mai bun mod pentru crearea de șiruri lungi este folosind StringBuilder() și append(), fie viteza va fi inacceptabil.

Comentarii (1)