我正在通过PMD规则AppendCharacterWithChar
。它说避免在StringBuffer.append中将字符串联为字符串。
StringBuffer sb = new StringBuffer();
// Avoid this
sb.append("a");
// use instead something like this
StringBuffer sb = new StringBuffer();
sb.append('a');
我真的需要这个PMD规则吗?以下两段代码之间有很大的性能差异吗?
String text = new StringBuffer().append("some string").append('c').toString();
String text = new StringBuffer().append("some string").append("c").toString();
答案 0 :(得分:12)
将字符附加为char
总是比将其添加为String
更快。
但性能差异是否重要?如果你只做一次,它就没有了。如果它在一个循环内重复它的身体一百万次,那么是的,它可能很重要。
如果您已在编译时拥有该字符,只需将其作为字符附加即可。如果它存储在String
类型的变量中,请不要打扰它,例如使用String.charAt(0)
或其他方式,只需添加String
。
在旁边注意:
将StringBuilder
课程归为StringBuffer
。 StringBuilder
更快,因为它的方法不同步(在大多数情况下你不需要)。
在旁注#2:
这不会编译:
String text = new StringBuffer().append("some string").append('c');
append()
返回StringBuffer
进行链接。你需要打电话给toString()
:
String text = new StringBuffer().append("some string").append('c').toString();
答案 1 :(得分:4)
出于好奇,我用jmh运行了微基准测试(包括GC监控)。使用字符串稍微慢一点,但差异很小:每次调用大约5 ns(纳秒),GC活动没有显着差异。
如果您拨打append("c")
而不是append('c')
一百万次,则会为您的计划增加5毫秒。
基准测试结果,包括gc time - n
表示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
代码:
@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);
}
}
答案 2 :(得分:2)
查看每个的实现并进行比较:
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;
}
当您可以选择使用两者时,您更喜欢哪一个?
如果我有1000行,我真的更喜欢使用append(char c)
来获得更好的表现,但对于一行来说,这并不重要。
答案 3 :(得分:0)
是的正确避免在StringBuffer.append 中将字符串联为字符串,因为无论何时编写sb.append("a")
都意味着您正在创建值为a
的String对象,而新的String意味着新的String对象和Stringpool中的新String对象,这意味着堆空间中不必要的空间容纳。