在 StringBuffer 追加中对单字符值使用字符而不是字符串

我正在通过PMD规则。它说避免在StringBuffer.append中将字符串联为字符串。AppendCharacterWithChar

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

答案 1

将字符追加为 a 始终比将其追加为 快。charString

但是,性能差异重要吗?如果你只做一次,它就不会。如果它在一个循环中重复它的身体一百万次,那么是的,它可能很重要。

如果在编译时已有该字符,只需将其追加为字符即可。如果它存储在带有类型的变量中,请不要费心访问它,例如使用或其他一些方式,只需附加.StringString.charAt(0)String

附注:

StringBuilder 类替换为 StringBuffer。 速度更快,因为它的方法不同步(在大多数情况下不需要同步)。StringBuilder

附注#2:

这不会编译:

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

append()返回链接。您需要调用它:StringBuffertoString()

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

答案 2

出于好奇,我用jmh运行了一个微基准测试(包括GC监测)。使用 String 稍微慢一些,但差异很小:每次调用约 5 ns(纳秒),GC 活性没有显著差异。

如果你调用 append(“c”) 而不是 append('c') 一百万次,它会给你的程序增加 5 毫秒。

基准测试结果(包括 gc 时间)表示 StringBuilder 的初始长度:n

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