2

我知道 concat、StringBuffer 和 StringBuilder 之间的区别。我知道 StringBuffer.toString 后备数组存在内存问题,它会导致内存爆炸。我什至知道 JDK Sun 优化包括为初始容量分配 2 的幂。

但我仍然想知道重用 StringBuffer(在 toString() 中使用)的最佳方法,或者重用 StringBuffer 是否相关。考虑到内存和速度性能,哪一个更好?

public String toString2() {
  StringBuffer sb = new StringBuffer(<size>) 
  ... several .append(stuff) ...
  sb.trimToSize()
  return sb.toString()
}

或者

private StringBuffer sb = new StringBuffer(<size>) 
public String toString2() {
  sb.delete()
  sb.setLength(1024)
  sb.trimToSize() 
  ... several .append(stuff) ...
  return sb.toString()
}

为什么?

4

4 回答 4

3

我想说第一个示例绝对更清晰、更易于阅读且更安全——这几乎总是胜过性能微优化问题。如果您确实有性能问题,并且您可以证明(通过具体测量)它是由这段代码引起的,并且您还可以通过测量证明在您的本地环境中,第二个示例明显更快,那么 - 和只有这样- 你有充分的理由使用它。

否则,您的问题没有一般性的答案。如果人们声称第二个示例平均快了n %,如果它在您的机器上没有任何区别(或者因为不同的环境,或者因为很少调用实际的代码片段,那么它会对您有什么帮助?在您的应用程序中)?

然而,我个人的直觉是,在现代 JVM 上(阅读:至少在 JDK6 上,但可能已经在 J​​DK5 上),第一个示例实际上可能更快,因为巧妙的 GC 技巧使得短期对象的分配/释放非常便宜的。特别是如果您使用 aStringBuilder而不是StringBuffer,它没有同步开销。

于 2012-04-16T09:02:06.920 回答
2
  1. 使用StringBuilder-- 的一种非同步变体,StringBuffer它可以直接为您提供开箱即用的性能提升。
  2. 无需调用trimToSize- 您只是强制StringBuilder创建一个新的 char 数组。
  3. StringBuilder由于 JVM 针对短期对象的分配进行了高度优化,因此通过重用 a 可以节省非常非常少的费用。
  4. StringBuilder仅当您无法在单个表达式中表达您的字符串时才使用而不是简单的连接表达式 - 例如,您正在迭代某些内容。使用 .concatenation 编译成基本相同的代码StringBuilder
于 2012-04-16T09:09:38.027 回答
0

如果性能是一个问题,我根本不会使用 StringBuffer 。

如果你不能确定只有一个线程会调用 toString(),我每次都会创建一个 StringBuilder。

您遇到的另一个问题是不同的数据类型会产生大量垃圾,例如int,或者double这会减少您可能获得的任何好处。

我不会回收 StringBuilder,我只会让事情变得简单。如果您真的需要最大性能,我会使用不同的解决方案,例如带有不会产生垃圾的库的直接 ByteBuffer。

于 2012-04-16T09:04:05.493 回答
0

尝试两者的性能测试。与所有性能问题一样,它取决于上下文以及方法的调用方式。

几点注意事项:

  • StringBuilder不是线程安全的,但在您的第一个示例中,线程安全不是问题,因为您的方法没有使用字段。这使得StringBuilderStringBuffer.
  • 您的第二种方法不是线程安全的,因此如果您有多个线程调用该方法,那么两个线程都将使用 safe StringBuffer,因此您最终会得到一个包含来自两个调用的元素的字符串。
  • 如果您保留StringBuffer周围(第二个示例),则取决于您使用该方法的频率,您实际上可能会保留更多内存,因为垃圾收集器不会收集 使用的内存StringBuffer,而这些内存可能会被其他对象使用。
于 2012-04-16T09:11:21.853 回答