2015-02-05 11 views
11

Stavo passando attraverso la regola PMD AppendCharacterWithChar. Dice Evita di concatenare i caratteri come stringhe in StringBuffer.append.Utilizzo del carattere anziché di String per i valori a carattere singolo in StringBuffer append

StringBuffer sb = new StringBuffer(); 
    // Avoid this 
    sb.append("a"); 

    // use instead something like this 
    StringBuffer sb = new StringBuffer(); 
    sb.append('a'); 

Ho davvero bisogno di questa regola PMD? C'è molta differenza di prestazioni tra i seguenti due pezzi di codice?

String text = new StringBuffer().append("some string").append('c').toString(); 

String text = new StringBuffer().append("some string").append("c").toString(); 
+0

c'è un buon motivo per voi di non concat caratteri come caratteri o è solo curiosità? – xmoex

+0

Sto implementando il controllo PMD per il mio progetto. Ci sono molti posti in cui le persone hanno usato un solo 'stringa' invece di' char' in 'StringBuffer' /' StringBuilder' append. Volevo solo sapere se valeva la pena di correggere la violazione 295 che ho ottenuto, o dovrei semplicemente ignorare questa regola. – Zeeshan

+0

quindi la ragione è essere pigri? Secondo me non ci sono scuse per non farlo nel modo giusto :-) potresti usare uno script con espressioni regolari per sistemarlo facilmente, forse il tuo IDE può farlo per te? https://regex101.com/r/yN3dE2/1 – xmoex

risposta

12

Aggiunta di un carattere come un char sarà sempre più veloce di aggiungendo come String.

Ma la differenza di prestazioni è importante? Se lo fai una sola volta, non lo fa. Se è all'interno di un ciclo che ripete il suo corpo un milione di volte, allora sì, potrebbe essere importante.

Se si dispone già del carattere in fase di compilazione, è sufficiente aggiungerlo come carattere. Se è memorizzato in una variabile con il tipo String, non preoccuparti di accedervi, ad es. con String.charAt(0) o altri modi, è sufficiente aggiungere lo String.

Su un lato nota:

favore della classe StringBuilder-StringBuffer. StringBuilder è più veloce perché i suoi metodi non sono sincronizzati (cosa che non è necessaria nella maggior parte dei casi).

Su un lato nota # 2:

Questo non verrà compilato:

String text = new StringBuffer().append("some string").append('c'); 

append() rendimenti StringBuffer per il concatenamento. È necessario chiamare toString() su di esso:

String text = new StringBuffer().append("some string").append('c').toString(); 
+0

grazie, mancato toString, lo correggerò :) – Zeeshan

+0

Come al solito con queste domande va: sì, forse, potrebbe essere un guadagno in termini di prestazioni. Fai un punto di riferimento sul tuo sistema, ottieni delle prove, poi decidi quanto impegno vale la pena di inserire per la tua situazione – avalancha

2

Vedi l'attuazione di ciascuno e confrontarli:

public AbstractStringBuilder append(char c):

public AbstractStringBuilder append(char c) { 
    int newCount = count + 1; 
    if (newCount > value.length) 
     expandCapacity(newCount); 
    value[count++] = c; 
    return this; 
} 

public AbstractStringBuilder append(String str):

public AbstractStringBuilder append(String str) { 
    if (str == null) str = "null"; 
    int len = str.length(); 
    if (len == 0) return this; 
    int newCount = count + len; 
    if (newCount > value.length) 
     expandCapacity(newCount); 
    str.getChars(0, len, value, count); 
    count = newCount; 
    return this; 
} 

Che uno si fa preferisci quando hai la possibilità di usare entrambi?

Se ho 1000 di linee, preferisco davvero utilizzare append(char c) per prestazioni migliori, ma per una riga, non ha molta importanza.

0

Sì, è giusto Evitare concatenazione di caratteri come stringhe in StringBuffer.append perché ogni volta che si scrive sb.append("a") significa che si sta creando un oggetto String con valore a e new String significa nuovo oggetto String e nuovo oggetto String in Stringpool e questo significa inutilmente sistemazione nello spazio dell'heap.

+1

'sb.append (" a ")' non crea nuove stringhe. I letterali delle stringhe sono intervenuti. – icza

+0

ma cosa succede se non ci sono oggetti stringa con valore 'a'? creerà un nuovo oggetto nel pool di stringhe? –

+0

Sì, se non è ancora nel pool, verrà aggiunto ad esso. Ma poiché il letterale 'String' fa parte della definizione della classe, potrebbe essere aggiunto al pool' String' quando la classe viene caricata (dipende dall'implementazione). – icza

3

Per curiosità ho eseguito un micro benchmark con jmh (incluso il controllo GC).L'utilizzo di una stringa è leggermente più lento ma la differenza è minima: circa 5 ns (nanosecondi) per invocazione e nessuna differenza significativa sull'attività del GC.

Se si chiama append("c") invece di append('c') un milione di volte, sarebbe aggiungere 5 ms al vostro programma.

risultati benchmark, tra cui tempo di GC - n rappresenta la lunghezza iniziale del StringBuilder:

Benchmark        (n) Mode Cnt  Score  Error Units 
SO28344.appendChar      0 avgt 30 16.476 ± 0.331 ns/op 
SO28343294.appendChar:·gc.time   0 avgt 30 256.000    ms 
SO28343294.appendString     0 avgt 30 22.048 ± 0.345 ns/op 
SO28343294.appendString:·gc.time  0 avgt 30 220.000    ms 

SO28343294.appendChar     50 avgt 30 17.323 ± 0.967 ns/op 
SO28343294.appendChar:·gc.time   50 avgt 30 67.000    ms 
SO28343294.appendString    50 avgt 30 20.944 ± 1.466 ns/op 
SO28343294.appendString:·gc.time  50 avgt 30 74.000    ms 

SO28343294.appendChar    1000 avgt 30 58.396 ± 0.811 ns/op 
SO28343294.appendChar:·gc.time  1000 avgt 30 25.000    ms 
SO28343294.appendString    1000 avgt 30 64.572 ± 4.779 ns/op 
SO28343294.appendString:·gc.time  1000 avgt 30 24.000    ms 

Codice:

@State(Scope.Thread) 
@BenchmarkMode(Mode.AverageTime) 
public class SO28343294 { 

    @Param({"0", "50", "1000"}) int n; 
    Random r = new Random(); 
    StringBuilder sb; 
    String s; 
    char c; 

    @Setup(Level.Invocation) public void populate() { 
    sb = new StringBuilder(n + 5); 
    for (int i = 0; i < n; i++) { 
     sb.append((char) (r.nextInt(26) + 'a')); 
    } 
    c = (char) (r.nextInt(26) + 'a'); 
    s = new String(new char[] { c }); 
    } 

    @Benchmark public StringBuilder appendString() { 
    return sb.append(s); 
    } 

    @Benchmark public StringBuilder appendChar() { 
    return sb.append(c); 
    } 
} 
Problemi correlati