Using character instead of String for single-character values in StringBuffer append

无人久伴 提交于 2020-01-24 02:18:07

问题


I was going through the PMD rule AppendCharacterWithChar. It says Avoid concatenating characters as strings in StringBuffer.append.

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

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

Do I really need this PMD rule? Is there much performance difference between the following two piece of code?

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

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

回答1:


Appending a character as a char will always be faster than appending it as a String.

But does the performance difference matter? If you just do it once, it doesn't. If it is inside a cycle repeating its body a million times, then yes, it might matter.

If you already have the character at compile time, just append it as a character. If it is stored in a variable with String type, don't bother accessing it e.g. with String.charAt(0) or some other ways, simply just append the String.

On a Side Note:

Favor the StringBuilder class to StringBuffer. StringBuilder is faster because its methods are not synchronized (which you don't need in most cases).

On a Side Note #2:

This won't compile:

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

append() returns StringBuffer for chaining. You need to call toString() on it:

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



回答2:


Out of curiosity I ran a micro benchmark with jmh (including GC monitoring). Using a String is marginally slower but the difference is minimal: around 5 ns (nanoseconds) per invocation and no significant difference on GC activity.

If you called append("c") instead of append('c') one million times, it would add 5 ms to your program.

Benchmark results, including gc time - n represents the initial length of the 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

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



回答3:


See the implementation of each and compare them:

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

Which one do you prefer when you have the option to use both?

If I have 1000s of lines, I will really prefer to use append(char c) for better performances, but for one line, it doesn't really matter.




回答4:


Yes its right Avoid concatenating characters as strings in StringBuffer.append because whenever you write sb.append("a") means you are creating a String object with value a and new String means new String object and new String object in Stringpool and that means unnecessarily space accommodation in heap space.



来源:https://stackoverflow.com/questions/28343294/using-character-instead-of-string-for-single-character-values-in-stringbuffer-ap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!