在StringBuffer追加中使用字符而不是String来表示单字符值

时间:2015-02-05 11:53:58

标签: java string optimization string-concatenation pmd

我正在通过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();

4 个答案:

答案 0 :(得分:12)

将字符附加为char总是比将其添加为String更快。

但性能差异是否重要?如果你只做一次,它就没有了。如果它在一个循环内重复它的身体一百万次,那么是的,它可能很重要。

如果您已在编译时拥有该字符,只需将其作为字符附加即可。如果它存储在String类型的变量中,请不要打扰它,例如使用String.charAt(0)或其他方式,只需添加String

在旁边注意:

StringBuilder课程归为StringBufferStringBuilder更快,因为它的方法不同步(在大多数情况下你不需要)。

在旁注#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对象,这意味着堆空间中不必要的空间容纳。