【问题标题】:Java : Clearing StringBuffer contentsJava:清除 StringBuffer 内容
【发布时间】:2011-05-02 23:16:15
【问题描述】:

全部,

我想知道使用 setLength(0) 清除 StringBuffer 内容是否有意义。即这样做更好吗:

while (<some condition>)
{
    stringBufferVariable = new StringBuffer(128);
    stringBufferVariable.append(<something>)
                        .append(<more>)
                        ... ;
    Append stringBufferVariable.toString() to a file;
    stringBufferVariable.setLength(0);
}

我的问题:
1 > 这仍然比使用 String 对象附加内容有更好的性能吗?

我不确定重新初始化 StringBuffer 变量会如何影响性能,从而影响问题。

请倒入你的cmets

[编辑]:删除了关于与 StringBuilder 进行比较的第二个问题,因为根据回复,我已经明白没有什么可研究的了。

【问题讨论】:

  • -1,问题1可以通过创建一个简单的测试用例来回答。问题 2 直接在 API 中解决。如果性能是一个问题,最后一个性能问题也可以通过创建一个测试类来解决。

标签: java stringbuilder stringbuffer


【解决方案1】:

比串联字符串好吗?

如果你问是否

stringBufferVariable.append("something")
                    .append("more");
...

会比与+ 连接性能更好,然后是的,通常情况下。这就是这些类存在的全部原因。与更新 char 数组中的值相比,创建对象的成本更高。

str = "something" + "more" + "..."; 等简单情况下,现在大多数编译器(如果不是所有编译器)都将字符串连接转换为使用 StringBuilder。然后我可以看到的唯一性能差异是编译器没有设置初始大小的优势。基准会告诉您差异是否足够重要。不过,使用+ 会使代码更具可读性。

根据我的阅读,编译器显然无法优化循环中完成的连接

String str = "";
for (int i = 0; i < 10000; i++) {
    str = str + i + ",";
}

所以在这些情况下,您仍然希望显式使用 StringBuilder。

StringBuilder 与 StringBuffer

StringBuilder 不是线程安全的,而StringBuffer 是线程安全的,但它们在其他方面是相同的。在 StringBuffer 中执行的同步使其变慢,因此StringBuilder 更快,除非您需要同步,否则应该使用它。

你应该使用setLength吗?

您的示例当前的编写方式我认为对setLength 的调用不会为您带来任何好处,因为您在每次通过循环时都会创建一个新的 StringBuffer。你真正应该做的是

StringBuilder sb = new StringBuilder(128);
while (<some condition>) {
    sb.append(<something>)
      .append(<more>)
      ... ;
    // Append stringBufferVariable.toString() to a file;
    sb.setLength(0);
}

这避免了不必要的对象创建,并且在这种情况下 setLength 只会更新一个内部 int 变量。

【讨论】:

  • 没有记下来但是第一部分是错误的,编译器会在那里使用一个StringBuilder。
  • @Tom - 谢谢,我不知道这种优化。做了一些研究并改进了我的答案。
【解决方案2】:

我只关注问题的这一部分。 (其他部分之前在SO上已经问过很多次了。)

我想知道使用 setLength(0) 清除 StringBuffer 内容是否有意义。

这取决于您使用的 Java 类库。在一些较旧的 Sun 版本的 Java 中,StringBuffer.toString() 的实现是假设对 sb.toString() 的调用是对缓冲区完成的最后一件事。 StringBuffer 的原始后备数组成为toString() 返回的字符串的一部分。随后尝试使用 StringBuffer 导致通过复制 String 内容创建和初始化一个新的支持数组。因此,在您的代码尝试重用 StringBuffer 时,实际上会使应用程序变慢。

对于 Java 1.5 及更高版本,更好的编码方式如下:

    bufferedWriter.append(stringBufferVariable);
    stringBufferVariable.setLength(0);

这应该将字符直接从 StringBuilder 复制到文件缓冲区中,而无需创建临时字符串。如果 StringBuffer 声明在循环之外,那么 setLength(0) 允许您重用缓冲区。

最后,只有当您有证据表明这部分代码是(或可能是)瓶颈时,您才应该担心所有这些。 “过早的优化是万恶之源”等等,等等。

【讨论】:

  • 注意:关于toString 的部分仅适用于Java 1.5 之前的版本!对于 Java 1.6 和 1.5,StringBuilder 和 StringBuffer 的 javadoc 声明:“......分配了一个新的 String 对象......然后返回此 String。对此序列的后续更改不会影响 String 的内容。”跨度>
  • @Carlos - Java 1.5 之前/之后的区别在于 toString() 的实现方式,而不是它的行为方式。
【解决方案3】:

对于问题 2,StringBuilder 会比 StringBuffer 表现更好。 StringBuffer 是线程安全的,这意味着方法是同步的。字符串生成器未同步。因此,如果您拥有的代码将由一个线程运行,那么 StringBuilder 将具有更好的性能,因为它没有进行同步的开销。

根据 camickr 的建议,请查看 StringBuffer 和 StringBuilder 的 API 了解更多信息。

另外你可能对这篇文章感兴趣:The Sad Tragedy of Micro-Optimization Theater

【讨论】:

    【解决方案4】:

    1 > 这仍然比使用 String 对象附加内容有更好的性能吗?

    是的,连接字符串很慢,因为您不断创建新的字符串对象。

    2 > 如果在这里使用 StringBuilder 会比 StringBuffer 执行得更好,为什么?

    您是否阅读过 StringBuilder 和/或 StringBuffer 的 API 说明?此问题已在此处解决。

    我不确定重新初始化 StringBuffer 变量会如何影响性能,从而影响问题。

    创建一个测试程序。创建一个测试,每次都会创建一个新的 StringBuffer/Builder。然后重新运行测试并将字符重置为 0,然后比较时间。

    【讨论】:

    • 是的,我做到了。 StringBuilder 未同步,因此性能优于 StringBuffer。只是想确定是否没有其他可能影响性能的因素。
    【解决方案5】:

    也许我误解了你的问题......如果你只是在每次迭代开始时创建一个新的长度,为什么要在底部将长度设置为 0?

    假设变量是方法的局部变量,或者如果在方法之外声明它不会被多个线程使用(如果它在方法之外,您的代码可能有问题)然后将其设为字符串生成器。

    如果您在循环之外声明 StringBuilder,那么您不需要每次进入循环时都创建一个新的,但您希望在循环结束时将长度设置为 0。

    如果您在循环内声明 StringBuilder,则无需在循环结束时将长度设置为 0。

    在循环外声明它并将长度设置为 0 可能会更快,但我会同时测量两者,如果差异不大,则在循环内声明变量。最好限制变量的范围。

    【讨论】:

      【解决方案6】:

      是的! setLength(0) 是个好主意!这就是它的用途。更快的方法是丢弃stringBuffer 并制作一个新的。它更快,不能说它是内存效率的:)

      【讨论】:

      • 如何创建一个新的缓冲区比重用现有的缓冲区更快?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-30
      • 2011-03-05
      • 2016-05-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多