2015-02-05 6 views
11

Ich ging durch die PMD-Regel AppendCharacterWithChar. Es sagt Vermeiden Sie die Verkettung von Zeichen als Zeichenfolgen in StringBuffer.append.Verwendung von Zeichen anstelle von String für Einzelzeichenwerte in StringBuffer append

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

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

Muss ich diese PMD Regel wirklich? Gibt es einen großen Leistungsunterschied zwischen den folgenden zwei Teilen des Codes?

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

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

ist es ein guter Grund für Sie nicht Zeichen als Zeichen zu verketten oder ist es nur Neugier? – xmoex

+0

Ich implementiere PMD-Check für mein Projekt. Es gibt viele Orte, an denen Leute eine einzelne "string" anstelle von "char" in "StringBuffer"/"StringBuilder" append verwendet haben. Ich wollte nur wissen, ob es den Schmerz wert ist, die Verletzung zu korrigieren, die ich bekommen habe, oder sollte ich diese Regel einfach ignorieren. – Zeeshan

+0

so ist der Grund faul? Meiner Meinung nach gibt es keine Entschuldigung dafür, es nicht richtig zu machen :-) Du könntest ein Skript mit regulären Ausdrücken verwenden, um es leicht zu beheben, vielleicht kann deine IDE es für dich tun? https://regex101.com/r/yN3dE2/1 – xmoex

Antwort

12

einen Charakter als char Anfügen wird immer schneller, als es als String anhängt.

Aber ist der Leistungsunterschied wichtig? Wenn Sie es nur einmal tun, tut es das nicht. Wenn es in einem Zyklus ist, der seinen Körper millionenfach wiederholt, dann könnte es ja von Bedeutung sein.

Wenn Sie das Zeichen bereits zur Kompilierzeit haben, fügen Sie es einfach als Zeichen an. Wenn es in einer Variablen mit dem Typ String gespeichert ist, sollte man nicht darauf zugreifen, z.B. mit String.charAt(0) oder auf andere Weise, einfach nur die String anhängen.

auf einer Seite Hinweis:

Favor die StringBuilder Klasse StringBuffer. StringBuilder ist schneller, weil seine Methoden nicht synchronisiert sind (die Sie in den meisten Fällen nicht benötigen).

auf einer Seite Hinweis # 2:

Dies wird nicht kompilieren:

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

append() kehrt StringBuffer für die Verkettung. Sie müssen toString() darauf nennen:

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

danke, verpasst toString, wird es korrigieren :) – Zeeshan

+0

Wie immer mit diesen Fragen geht es: ja, vielleicht, könnte ein Leistungsgewinn sein. Mache einen Benchmark auf deinem System, besorge dir einen Beweis und entscheide dann, wie viel Aufwand es wert ist, für deine Situation eingesetzt zu werden. – avalancha

2

die Umsetzung jeder See und vergleichen sie:

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

Welche Sie tun bevorzugen Sie, wenn Sie beide Optionen verwenden können?

Wenn ich 1000s Linien habe, werde ich wirklich lieber append(char c) für bessere Leistungen verwenden, aber für eine Linie ist es nicht wirklich wichtig.

0

Ja sein Recht Vermeiden Sie Zeichen als Zeichenfolge in StringBuffer.append verketten, weil, wenn Sie schreiben sb.append("a") bedeuten, dass Sie a und neuen String ein String-Objekt mit dem Wert schaffen bedeutet neues String-Objekt und neues String-Objekt in Stringpool und das bedeutet unnötig Platz Unterkunft im Heap-Raum.

+1

'sb.append (" a ")' erstellt keine neuen Strings. String-Literale sind interened. – icza

+0

aber was, wenn es kein String-Objekt mit dem Wert 'a' gibt? wird das ein neues Objekt im String-Pool erstellen? –

+0

Ja, wenn es noch nicht im Pool ist, wird es hinzugefügt. Da das String-Literal jedoch Teil der Klassendefinition ist, kann es beim Laden der Klasse (abhängig von der Implementierung) zum Pool "String" hinzugefügt werden. – icza

3

Aus Neugierde führte ich einen Mikro-Benchmark mit JMH (einschließlich GC-Monitoring).Die Verwendung eines Strings ist marginal langsamer, aber der Unterschied ist minimal: etwa 5 ns (Nanosekunden) pro Aufruf und kein signifikanter Unterschied in der GC-Aktivität.

Wenn Sie append("c") genannt statt append('c') eine Million Mal, wäre es 5 ms zu Ihrem Programm hinzufügen.

Benchmark-Ergebnisse, einschließlich gc Zeit - n stellen die Anfangslänge des String:

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 

Code:

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