StringBuilder vs Concatenazione di stringhe in toString () in Java


925

Date le 2 toString()implementazioni di seguito, quale è preferita:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

o

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

Ancora più importante, dato che abbiamo solo 3 proprietà, potrebbe non fare la differenza, ma a che punto +passeresti da Concat a StringBuilder?


40
A che punto passi a StringBuilder? Quando influisce sulla memoria o sulle prestazioni. O quando potrebbe. Se lo fai davvero solo per un paio di stringhe una volta, non preoccuparti. Ma se lo farai più e più volte, dovresti vedere una differenza misurabile quando usi StringBuilder.
ewall,

qual è la media di 100 nel parametro?
Asif Mushtaq

2
@UnKnown 100 è la dimensione iniziale di StringBuilder
non sequitor

@nonsequitor Quindi i caratteri massimi saranno 100?
Asif Mushtaq,

10
@Sconosciuta non solo la dimensione iniziale, se conosci la dimensione approssimativa della stringa con cui hai a che fare, allora puoi dire StringBuilderquante dimensioni allocare in anticipo altrimenti, se esaurisce lo spazio, dovrà raddoppiare la dimensione creando un il nuovo char[]array copia quindi i dati, il che è costoso. Puoi imbrogliare dando la dimensione e quindi non è necessario per questa creazione di array - quindi se pensi che la tua stringa avrà una lunghezza di ~ 100 caratteri, puoi impostare StringBuilder su quella dimensione e non dovrà mai espandersi internamente.
non sequencer

Risposte:


964

La versione 1 è preferibile perché è più corta e il compilatore infatti la trasformerà in versione 2 - nessuna differenza di prestazioni.

Ancora più importante, dato che abbiamo solo 3 proprietà che potrebbero non fare la differenza, ma a che punto passi da concat a builder?

Nel punto in cui ti stai concatenando in un ciclo, di solito è quando il compilatore non può sostituire StringBuilderda solo.


19
Questo è vero ma il riferimento linguistico afferma anche che questo è facoltativo. In effetti, ho appena fatto un semplice test con JRE 1.6.0_15 e non ho visto alcuna ottimizzazione del compilatore nella classe decompilata.
bruno conde,

37
Ho appena provato il codice dalla domanda (compilato su JDK 1.6.0_16) e ho trovato l'ottimizzazione come previsto. Sono abbastanza sicuro che tutti i compilatori moderni lo faranno.
Michael Borgwardt,

22
Hai ragione. Guardando il bytecode posso vedere chiaramente l'ottimizzazione di StringBuilder. Stavo usando un decompilatore e, in qualche modo, si sta convertendo nuovamente in concat. +1
bruno conde,

80
Non per battere un cavallo morto, ma la dicitura nelle specifiche è: To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.la parola chiave che può esserci . Dato che questo è ufficialmente facoltativo (anche se molto probabilmente implementato) non dovremmo proteggerci?
Lucas,

93
@Lucas: No, non dovremmo. Se il compilatore decide di non eseguire tale ottimizzazione, sarà perché non ne vale la pena. Nel 99% dei casi, il compilatore sa meglio quale ottimizzazione valga la pena, quindi come regola generale lo sviluppatore non dovrebbe interferire. Ovviamente, la tua situazione potrebbe cadere nell'altro 1%, ma ciò può essere verificato solo tramite (attenta) analisi comparativa.
sleske,

255

La chiave è se stai scrivendo una singola concatenazione in un unico posto o accumulandola nel tempo.

Per l'esempio che hai dato, non ha senso usare esplicitamente StringBuilder. (Guarda il codice compilato per il tuo primo caso.)

Ma se stai costruendo una stringa, ad esempio all'interno di un ciclo, usa StringBuilder.

Per chiarire, supponendo che hugeArray contenga migliaia di stringhe, codice come questo:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

è molto dispendioso in termini di tempo e memoria rispetto a:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();

3
Sì, StringBuilder non ha bisogno di ricreare ripetutamente l'oggetto String.
Olga,

124
Dannazione, ho usato quelle 2 funzioni per testare una grande stringa su cui sto lavorando. 6.51min vs 11secs
user1722791

1
A proposito, puoi usare result += s;anche (nel primo esempio)
RAnders00

1
quanti oggetti saranno creati da questa affermazione? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
Asif Mushtaq

1
Che dire di qualcosa del tipo: String str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? c: c '+ ...; ? Ciò impedirà l'ottimizzazione?
tra il

75

Preferisco:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

... perché è breve e leggibile.

Vorrei non ottimizzare questo per la velocità a meno che non lo si utilizza all'interno di un ciclo con un numero molto elevato di ripetizione e aver misurato la differenza di prestazioni.

Concordo sul fatto che se si devono produrre molti parametri, questo modulo può creare confusione (come dice uno dei commenti). In questo caso passerei a una forma più leggibile (forse usando ToStringBuilder di apache-commons - preso dalla risposta di matt b) e ignorerei di nuovo le prestazioni.


64
In realtà è più lungo, contiene più simboli e ha variabili un testo fuori sequenza.
Tom Hawtin - tackline

4
Quindi diresti che è meno leggibile di uno degli altri approcci?
Tangens,

3
Preferisco scrivere questo, perché è più facile aggiungere più variabili, ma non sono sicuro che sia più leggibile, specialmente quando il numero di argomenti aumenta. Inoltre, non funziona nei momenti in cui è necessario aggiungere bit in momenti diversi.
Alex Feinman,

78
Sembra più difficile da leggere (per me). Ora devo scansionare avanti e indietro tra {...} e i parametri.
Steve Kuo,

10
Preferisco questo modulo a, perché è sicuro se uno dei parametri ènull
rds

74

Nella maggior parte dei casi, non vedrai un'effettiva differenza tra i due approcci, ma è facile costruire uno scenario peggiore come questo:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

L'output è:

slow elapsed 11741 ms
fast elapsed 7 ms

Il problema è che + = accoda a una stringa ricostruisce una nuova stringa, quindi costa qualcosa di lineare rispetto alla lunghezza delle stringhe (somma di entrambe).

Quindi - alla tua domanda:

Il secondo approccio sarebbe più veloce, ma è meno leggibile e più difficile da mantenere. Come ho detto, nel tuo caso specifico probabilmente non vedresti la differenza.


Non dimenticare di .concat (). Immagino che il tempo trascorso sia compreso tra 10 e 18 ms, rendendolo trascurabile quando si usano stringhe brevi come nell'esempio post originale.
Droo,

9
Mentre hai ragione +=, l'esempio originale era una sequenza di +, che il compilatore trasforma in una singola string.concatchiamata. I tuoi risultati non si applicano.
Blindy,

1
@Blindy & Droo: - Entrambi avete ragione. L'uso di .concate in questo scenario è la soluzione migliore, poiché + = crea un nuovo oggetto ogni volta che viene eseguita la routine del ciclo.
Perilbrain,

3
sai che il suo toString () non è chiamato in un ciclo?
Omry Yadan,

Ho provato questo esempio per testare la velocità. Quindi i miei risultati sono: lento trascorso 29672 ms; tempo trascorso 15 ms. Quindi la risposta è ovvia. Ma se fossero 100 iterazioni - il tempo è lo stesso - 0 ms. Se 500 iterazioni - 16 ms e 0 ms. E così via.
Ernestas Gruodis,

28

Ho anche avuto uno scontro con il mio capo sul fatto se usare Append o +. Come stanno usando Append (non riesco ancora a capire come dicono ogni volta che viene creato un nuovo oggetto). Quindi ho pensato di fare un po 'di ricerca e sviluppo, anche se adoro la spiegazione di Michael Borgwardt, ma volevo solo mostrare una spiegazione se qualcuno avrà davvero bisogno di sapere in futuro.

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

e il disassemblaggio della classe precedente risulta come

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

Dai due codici precedenti puoi vedere che Michael ha ragione, in ogni caso viene creato un solo oggetto SB.


27

A partire da Java 1.5, la semplice concatenazione di una riga con "+" e StringBuilder.append () genera esattamente lo stesso bytecode.

Quindi, per motivi di leggibilità del codice, usa "+".

2 eccezioni:

  • ambiente multithread: StringBuffer
  • concatenazione in loop: StringBuilder / StringBuffer

22

Utilizzando l'ultima versione di Java (1.8) il disassembly ( javap -c) mostra l'ottimizzazione introdotta dal compilatore. +inoltre sb.append()genererà un codice molto simile. Tuttavia, sarà utile esaminare il comportamento se si utilizza +in un ciclo for.

Aggiunta di stringhe utilizzando + in un ciclo for

Giava:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode :( forestratto del ciclo)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

Aggiunta di stringhe utilizzando stringbuilder.append

Giava:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe :( forestratto del ciclo)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

C'è comunque un po 'di evidente differenza . Nel primo caso, dove è +stato utilizzato, StringBuilderviene creato nuovo per ciascuno per l'iterazione del ciclo e il risultato generato viene memorizzato effettuando una toString()chiamata (da 29 a 41). Quindi stai generando stringhe intermedie di cui non hai davvero bisogno mentre usi l' +operatore in forloop.


1
Questo è Oracle JDK o OpenJDK?
Christophe Roussy,

12

In Java 9 la versione 1 dovrebbe essere più veloce perché viene convertita in invokedynamicchiamata. Maggiori dettagli sono disponibili in JEP-280 :

L'idea è di sostituire l'intera append dance di StringBuilder con una semplice chiamata invocata a java.lang.invoke.StringConcatFactory, che accetterà i valori che necessitano di concatenazione.


9

Per motivi di prestazioni, l'uso di +=( Stringconcatenazione) è scoraggiato. Il motivo è: Java Stringè immutabile, ogni volta che viene fatta una nuova concatenazione viene Stringcreata una nuova (la nuova ha un'impronta digitale diversa dalla precedente già nel pool di stringhe ). La creazione di nuove stringhe mette sotto pressione il GC e rallenta il programma: la creazione di oggetti è costosa.

Il codice seguente dovrebbe renderlo più pratico e chiaro allo stesso tempo.

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

I risultati di una corsa sono riportati di seguito.

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

Non considerando i risultati per 1 concatenazione (JIT non stava ancora facendo il suo lavoro), anche per 10 concatenazioni la penalità di esecuzione è rilevante; per migliaia di concatenazioni, la differenza è enorme.

Lezioni apprese da questo esperimento molto rapido (facilmente riproducibile con il codice sopra): non usare mai il +=per concatenare le stringhe insieme, anche in casi molto elementari in cui sono necessarie alcune concatenazioni (come detto, creare nuove stringhe è comunque costoso e mette pressione sul GC).


9

Vedi l'esempio seguente:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

Produzione:

versione bytecode java: 8
java.version: 1.8.0_144
[str1.concat (str2)]
tempo medio per 10000 concatenazioni: 0,096 ms
tempo medio avg per 10000 concatenazioni: 0,185 ms
tempo medio avg per 10000 concatenazioni: 0,327 ms
tempo medio per 10000 concatenazioni: 0,501 ms
tempo medio avg per 10000 concatenazioni: 0,656 ms avg
Stringa creata di lunghezza: 1950000 in 17745 ms
[str1 + = str2]
tempo medio per 10000 concatenazioni: 0,21 ms
tempo medio per 10000 concatenazioni: 0,652 ms
tempo medio per 10000 concatenazioni: 1.129 ms
tempo medio per 10000 concatenazioni: 1.727 ms media
tempo medio per 10000 concatenazioni: 2.302 ms media
stringa creata di lunghezza: 1950000 in 60279 ms
[str1.append (str2)]
tempo medio per 10000 concatenazioni: 0,002 ms
tempo medio medio per 10000 concatenazioni: 0,002 ms
tempo medio medio per 10000 concatenazioni: 0,002 ms
tempo medio avg per 10000 concatenazioni: 0,002 ms
tempo medio avg per 10000 concatenazioni: 0,002 ms avg
Stringa creata di lunghezza: 1950000 in 100 ms

All'aumentare della lunghezza della stringa, aumenta anche il tempo di concatenazione.
Ecco dove StringBuilderè assolutamente necessario.
Come vedi, la concatenazione:, UUID.randomUUID()+"---"non influisce davvero sul tempo.

PS: Non penso quando usare StringBuilder in Java sia davvero un duplicato di questo.
Questa domanda parla di toString()quale il più delle volte non esegue concatenazioni di stringhe enormi.


Aggiornamento 2019

Da allora le java8cose sono cambiate un po '. Sembra che ora (java13), il tempo di concatenazione +=sia praticamente uguale a str.concat(). Tuttavia, il StringBuildertempo di concatenazione è ancora costante . (Il post originale sopra è stato leggermente modificato per aggiungere un output più dettagliato)

versione bytecode java: 13
java.version: 13.0.1
[str1.concat (str2)]
tempo medio per 10000 concatenazioni: 0,047 ms
tempo medio per 10000 concatenazioni: 0,1 ms
tempo medio per 10000 concatenazioni: 0,17 ms
tempo medio per 10000 concatenazioni: 0,255 ms
tempo medio avg per 10000 concatenazioni: 0,336 ms avg
Stringa di lunghezza creata: 1950000 in 9147 ms
[str1 + = str2]
tempo medio per 10000 concatenazioni: 0,037 ms
tempo medio avg per 10000 concatenazioni: 0,097 ms
tempo medio per 10000 concatenazioni: 0,249 ms
media tempo per 10000 concatenazioni: 0,298 ms media
tempo medio per 10000 concatenazioni: 0,326 ms tempo medio
creato stringa di lunghezza: 1950000 in 10191 ms
[str1.append (str2)]
tempo medio per 10000 concatenazioni: 0,001 ms
tempo medio medio per 10000 concatenazioni: 0,001 ms
tempo medio medio per 10000 concatenazioni: 0,001 ms
tempo medio avg per 10000 concatenazioni: 0,001 ms
tempo medio avg per 10000 concatenazioni: 0,001 ms avg
Stringa creata di lunghezza: 1950000 in 43 ms

Vale la pena notare anche che la bytecode:8/java.version:13combinazione ha un buon vantaggio in termini di prestazioni rispetto abytecode:8/java.version:8


Questa dovrebbe essere la risposta accettata .. dipende dalla dimensione di String Stream che determina la scelta di concat o StringBuilder
user1428716

@utente1428716 FYI: risposta aggiornata con risultati da java13cui ora sono diversi. Ma penso che la conclusione principale rimanga la stessa.
Marinos Il

7

Apache Commons-Lang ha una classe ToStringBuilder che è super facile da usare. Fa un buon lavoro sia nella gestione della logica append che nella formattazione di come vuoi che toString appaia.

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

Restituirà un output simile com.blah.YourClass@abc1321f[a=whatever, b=foo].

O in una forma più condensata usando il concatenamento:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

O se vuoi usare la riflessione per includere tutti i campi della classe:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

Se lo desideri, puoi anche personalizzare lo stile di ToString.


4

Rendi il metodo toString il più leggibile possibile!

L'unica eccezione per questo nel mio libro è se puoi dimostrarmi che consuma risorse significative :) (Sì, questo significa profilazione)

Si noti inoltre che il compilatore Java 5 genera un codice più veloce dell'approccio "StringBuffer" scritto a mano utilizzato nelle versioni precedenti di Java. Se usi "+" questo e i miglioramenti futuri sono gratuiti.


3

Sembra esserci un dibattito sul fatto che l'utilizzo di StringBuilder sia ancora necessario con gli attuali compilatori. Quindi ho pensato di dare i miei 2 centesimi di esperienza.

Ho un JDBCset di risultati di 10k record (sì, ho bisogno di tutti in un batch.) L'uso dell'operatore + richiede circa 5 minuti sulla mia macchina con Java 1.8. L'utilizzo stringBuilder.append("")richiede meno di un secondo per la stessa query.

Quindi la differenza è enorme. All'interno di un ciclo StringBuilderè molto più veloce.


2
Penso che il dibattito riguardi il suo utilizzo al di fuori dei loop. Penso che sia necessario un consenso per usarlo all'interno di un ciclo.
Giordania,

2

La concatenazione di stringhe in termini di prestazioni usando '+' è più costosa perché deve creare una copia completamente nuova di String poiché le stringhe sono immutabili in java. Questo gioca un ruolo particolare se la concatenazione è molto frequente, ad esempio: all'interno di un loop. Di seguito è ciò che la mia IDEA suggerisce quando provo a fare una cosa del genere:

inserisci qui la descrizione dell'immagine

Regole generali:

  • All'interno di una singola assegnazione di stringhe, l'utilizzo della concatenazione di stringhe va bene.
  • Se esegui un ciclo continuo per creare un grande blocco di dati carattere, scegli StringBuffer.
  • Usare + = su una stringa sarà sempre meno efficiente rispetto all'utilizzo di uno StringBuffer, quindi dovrebbe suonare campanelli di avvertimento - ma in alcuni casi l'ottimizzazione ottenuta sarà trascurabile rispetto ai problemi di leggibilità, quindi usa il tuo buon senso.

Ecco un bel blog di Jon Skeet su questo argomento.


2
Non dovresti mai usare StringBuffer a meno che tu non abbia assolutamente bisogno dell'accesso sincronizzato da più thread. Altrimenti preferisci StringBuilder che non è sincronizzato e quindi ha un sovraccarico minore.
Erki der Loony,

1

Posso sottolineare che se stai andando a scorrere su una raccolta e usi StringBuilder, potresti voler dare un'occhiata a Apache Commons Lang e StringUtils.join () (in diversi gusti)?

Indipendentemente dalle prestazioni, ti risparmierà la creazione di StringBuilders e dei loop per quella che sembra la milionesima volta.


1

Ecco cosa ho controllato in Java8

  • Utilizzo della concatenazione di stringhe
  • Utilizzando StringBuilder

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

È davvero un incubo se si utilizza la concatenazione di stringhe per un gran numero di stringhe.

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

Penso che dovremmo andare con l'approccio append StringBuilder. Il motivo è:

  1. Il concatenato di stringhe creerà ogni volta un nuovo oggetto stringa (poiché String è un oggetto immutabile), quindi creerà 3 oggetti.

  2. Con String builder verrà creato un solo oggetto [StringBuilder è mutabile] e l'ulteriore stringa verrà aggiunta ad esso.


Perché questa risposta è sottovalutata? docs.oracle.com/javase/8/docs/api/java/util/stream/… - Riduzione mutabile
user1428716

-4

Per stringhe semplici come quelle che preferisco usare

"string".concat("string").concat("string");

In ordine, direi che il metodo preferito per costruire una stringa sta usando StringBuilder, String # concat (), quindi l'operatore overload +. StringBuilder è un significativo aumento delle prestazioni quando si lavora su stringhe di grandi dimensioni proprio come l'utilizzo dell'operatore + è una grande riduzione delle prestazioni (riduzione esponenzialmente elevata all'aumentare della dimensione della stringa). L'unico problema con l'utilizzo di .concat () è che può generare NullPointerExceptions.


9
L'uso di concat () avrà probabilmente prestazioni peggiori di '+' poiché JLS consente di convertire '+' in StringBuilder, e molto probabilmente tutte le JVM lo fanno o usano un'alternativa più efficiente - lo stesso non è verosimilmente vero concat che deve creare e scartare almeno una stringa intermedia completa nel tuo esempio.
Lawrence Dol,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.