Domanda

Supponendo la stringa aeb:

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

Sotto il cofano, sono la stessa cosa?

Ecco concat decompilato come riferimento.Mi piacerebbe essere in grado di decompilare il file + anche l'operatore per vedere cosa fa.

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);
    }
}
È stato utile?

Soluzione

No, non proprio.

Innanzitutto, c'è una leggera differenza nella semantica.Se a È null, Poi a.concat(b) lancia a NullPointerException Ma a+=b tratterà il valore originale di a come se lo fosse null.Inoltre, il concat() il metodo accetta solo String valori mentre il + L'operatore convertirà silenziosamente l'argomento in una stringa (utilizzando l' toString() metodo per oggetti).Così il concat() il metodo è più rigoroso in ciò che accetta.

Per guardare dietro il cofano, scrivi una lezione semplice con a += b;

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

Ora smonta con javap -c (incluso nel Sun JDK).Dovresti vedere un elenco che include:

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

COSÌ, a += b è l'equivalente di

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

IL concat il metodo dovrebbe essere più veloce.Tuttavia, con più stringhe il StringBuilder il metodo vince, almeno in termini di prestazioni.

Il codice sorgente di String E StringBuilder (e la sua classe base privata del pacchetto) è disponibile in src.zip di Sun JDK.Puoi vedere che stai costruendo un array di caratteri (ridimensionando secondo necessità) e poi buttandolo via quando crei il file finale String.In pratica l'allocazione della memoria è sorprendentemente veloce.

Aggiornamento: Come osserva Pawel Adamski, le prestazioni sono cambiate nel più recente HotSpot. javac produce ancora esattamente lo stesso codice, ma il compilatore bytecode imbroglia.Il test semplice fallisce completamente perché l'intero corpo del codice viene buttato via.Riassumendo System.identityHashCode (non String.hashCode) mostra il StringBuffer il codice ha un leggero vantaggio.Soggetto a modifiche al rilascio del prossimo aggiornamento o se si utilizza una JVM diversa.Da @lukaseder, un elenco degli elementi intrinseci della JVM HotSpot.

Altri suggerimenti

Niyaz è corretto, ma vale anche la pena notare che l'operatore speciale + può essere convertito in qualcosa di più efficiente dal compilatore Java.Java ha una classe StringBuilder che rappresenta una stringa mutabile e non thread-safe.Quando si eseguono numerose concatenazioni di stringhe, il compilatore Java esegue la conversione silenziosamente

String a = b + c + d;

in

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

che per stringhe di grandi dimensioni è significativamente più efficiente.Per quanto ne so, questo non accade quando si utilizza il metodo concat.

Tuttavia, il metodo concat è più efficiente quando si concatena una stringa vuota su una stringa esistente.In questo caso, la JVM non ha bisogno di creare un nuovo oggetto String e può semplicemente restituire quello esistente.Vedere la documentazione concat per confermare questo.

Quindi, se sei molto preoccupato per l'efficienza, dovresti usare il metodo concat quando concatena stringhe possibilmente vuote e usare + altrimenti.Tuttavia, la differenza di prestazioni dovrebbe essere trascurabile e probabilmente non dovresti mai preoccuparti di questo.

Ho eseguito un test simile a quello di @marcio ma con il seguente ciclo:

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
}

Giusto per buona misura, mi sono buttato dentro StringBuilder.append() anche.Ogni test è stato eseguito 10 volte, con 100.000 ripetizioni per ciascuna corsa.Ecco i risultati:

  • StringBuilder vince a mani basse.Il risultato del tempo dell'orologio è stato 0 per la maggior parte delle esecuzioni e la più lunga ha richiesto 16 ms.
  • a += b impiega circa 40000ms (40s) per ogni esecuzione.
  • concat richiede solo 10.000 ms (10 secondi) per esecuzione.

Non ho ancora decompilato la classe per vedere gli interni o eseguirla tramite il profiler, ma sospetto a += b passa gran parte del tempo a creare nuovi oggetti StringBuilder e poi riconvertirli in String.

La maggior parte delle risposte qui risalgono al 2008.Sembra che le cose siano cambiate nel tempo.I miei ultimi benchmark effettuati con JMH mostrano che su Java 8 + è circa due volte più veloce di concat.

Il mio punto di riferimento:

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

Risultati:

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 ha ragione nel descrivere esattamente cosa fa l'operatore +.Crea un temporaneo StringBuilder, aggiunge le parti e termina con toString().

Tuttavia, tutte le risposte finora ignorano gli effetti delle ottimizzazioni del runtime di HotSpot.Nello specifico, queste operazioni temporanee vengono riconosciute come uno schema comune e vengono sostituite con un codice macchina più efficiente in fase di esecuzione.

@marcio:Hai creato un file micro-punto di riferimento;con le moderne JVM questo non è un modo valido per profilare il codice.

Il motivo per cui l'ottimizzazione in fase di esecuzione è importante è che molte di queste differenze nel codice, inclusa la creazione di oggetti, sono completamente diverse una volta avviato HotSpot.L'unico modo per saperlo con certezza è profilare il tuo codice sul posto.

Infine, tutti questi metodi sono infatti incredibilmente veloci.Questo potrebbe essere un caso di ottimizzazione prematura.Se hai un codice che concatena molto stringhe, il modo per ottenere la massima velocità probabilmente non ha nulla a che fare con gli operatori che scegli ma piuttosto con l'algoritmo che stai utilizzando!

Che ne dici di qualche semplice test?Utilizzato il codice seguente:

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);
  • IL "a + b" versione eseguita in 2500 ms.
  • IL a.concat(b) giustiziato nel 1200 ms.

Testato più volte.IL concat() l'esecuzione della versione ha richiesto in media la metà del tempo.

Questo risultato mi ha sorpreso perché il concat() crea sempre una nuova stringa (restituisce un "new String(result)".E' noto che:

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

Perché il compilatore non è stato in grado di ottimizzare la creazione della stringa nel codice "a + b", sapendo che il risultato era sempre la stessa stringa?Potrebbe evitare la creazione di una nuova stringa.Se non credi all'affermazione di cui sopra, verifica tu stesso.

Fondamentalmente, ci sono due importanti differenze tra + e the concat metodo.

  1. Se stai utilizzando il concat metodo allora sarai in grado di concatenare stringhe solo mentre in caso di + operatore, puoi anche concatenare la stringa con qualsiasi tipo di dati.

    Per esempio:

    String s = 10 + "Hello";
    

    In questo caso, l'output dovrebbe essere 10Ciao.

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

    Nel caso precedente è necessario fornire due stringhe obbligatorie.

  2. La seconda e principale differenza tra + E concat è questo:

    Caso 1:Supponiamo di concatenare le stesse stringhe con concat operatore in questo modo

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

    In questo caso il numero totale di oggetti creati nel pool è 7 in questo modo:

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy
    

    Caso 2:

    Ora concatenerò le stesse stringhe tramite + operatore

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

    Nel caso precedente il numero totale di oggetti creati è solo 5.

    In realtà quando concatiniamo le stringhe via + operatore quindi mantiene una classe StringBuffer per eseguire la stessa attività come segue: -

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

    In questo modo creerà solo cinque oggetti.

Quindi ragazzi, queste sono le differenze fondamentali tra + e il concat metodo.Godere :)

Per completezza volevo aggiungere che la definizione dell'operatore '+' si trova nel file JLS SE8 15.18.1:

Se una sola espressione dell'operando è di tipo String, la conversione della stringa (§5.1.11) viene eseguita sull'altro operando per produrre una stringa in fase di esecuzione.

Il risultato della concatenazione delle stringhe è un riferimento a un oggetto stringa che è la concatenazione delle due stringhe operand.I caratteri dell'operando a sinistra precedono i caratteri dell'operando a destra nella stringa appena creata.

L'oggetto stringa viene appena creato (§12.5) a meno che l'espressione non sia un'espressione costante (§15.28).

Per quanto riguarda l'implementazione, la JLS afferma quanto segue:

Un'implementazione può scegliere di eseguire conversione e concatenazione in un solo passaggio per evitare la creazione e quindi scartare un oggetto stringa intermedio.Per aumentare le prestazioni della concatenazione delle stringhe ripetute, un compilatore Java può utilizzare la classe StringBuffer o una tecnica simile per ridurre il numero di oggetti di stringa intermedia creati dalla valutazione di un'espressione.

Per i tipi primitivi, un'implementazione può anche ottimizzare la creazione di un oggetto wrapper convertendo direttamente da un tipo primitivo a una stringa.

Quindi, a giudicare da "un compilatore Java può utilizzare la classe StringBuffer o una tecnica simile per ridurre", compilatori diversi potrebbero produrre codice byte diverso.

IL + operatore può funzionare tra una stringa e un valore di tipo dati stringa, carattere, intero, doppio o float.Converte semplicemente il valore nella sua rappresentazione di stringa prima della concatenazione.

IL operatore concat può essere fatto solo su e con le stringhe.Verifica la compatibilità dei tipi di dati e genera un errore se non corrispondono.

Tranne questo, il codice che hai fornito fa le stesse cose.

Non credo.

a.concat(b) è implementato in String e penso che l'implementazione non sia cambiata molto rispetto alle prime macchine Java.IL + l'implementazione dell'operazione dipende dalla versione Java e dal compilatore.Attualmente + è implementato utilizzando StringBuffer per rendere l'operazione il più veloce possibile.Forse in futuro questo cambierà.Nelle versioni precedenti di Java + l'operazione su Strings è stata molto più lenta poiché ha prodotto risultati intermedi.

immagino che += è implementato utilizzando + e similmente ottimizzato.

Quando si utilizza +, la velocità diminuisce all'aumentare della lunghezza della stringa, ma quando si utilizza concat, la velocità è più stabile e l'opzione migliore è utilizzare la classe StringBuilder che ha una velocità stabile per farlo.

Immagino che tu possa capire il perché.Ma il modo migliore per creare stringhe lunghe è usare StringBuilder() e append(), entrambe le velocità saranno inaccettabili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top