问题
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