循环后清除字符串缓冲区/生成器


122

如何在循环后清除Java中的字符串缓冲区,以便下次迭代使用清除的字符串缓冲区?


2
另一个问题:为什么只将SB用于循环的一次迭代?还有另一个内部迭代吗?如果您只是想做A + B + C + D,那么SB不值得(Java编译器将在内部使用SB)。如果您想有条件地添加字符串的一部分,那么它会有所帮助,否则...只需使用“ +”即可。
helios 2010年

Answers:


135

一种选择是使用delete方法,如下所示:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

另一个选项(位清除器)使用setLength(int len)

sb.setLength(0);

有关更多信息,请参见Javadoc


15
少一些垃圾的办法就是在循环内部声明StringBuffer。
Mark Elliot'2

11
嗯,我认为sb.setLength(0); 比在循环中声明它更干净,更高效。您的解决方案违背了使用StringBuffer的性能优势...
Jon 2010年

6
我认为性能优势来自字符串可变性,而不是来自保存实例化。以下是对1e8迭代的快速测试:内循环(2.97s):ideone.com/uyyTL14w,外循环(2.87s):ideone.com/F9lgsIxh
Mark Elliot 2010年

6
谈到性能:除非在多线程方案中访问您的代码,否则应使用StringBuilder而不是StringBuffer -请参阅javadoc:“在可能的情况下,建议使用此类优先于StringBuffer,因为在大多数实现”。
RahelLüthy

4
在外部创建SB的唯一好处是不会丢失其内部(可能较长)的char []。如果在第一个迭代器中足够长,则第二个循环将不需要调整任何char []的大小。但是为了获得优势,“清除方法”将必须保留内部数组的大小。setLength会这样做,但它还会将SB中所有未使用的字符设置为\ u0000,因此,仅创建一个具有良好初始容量的新SB的性能就较低。在循环内声明更好。
helios 2010年

48

重用的最简单方法StringBuffer是使用方法setLength()

public void setLength(int newLength)

你可能有这样的情况

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb

2
@MMahmoud,在代码示例中,它应改为setLength而不是setlength
OnaBai 2012年

当我看到setLength代码源时,它是如何工作的(gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1),这里的count将始终大于newLength(0)?
Thomas Decaux

20

您有两种选择:

可以使用:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

或使用:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

注意

避免在循环中声明StringBufferStringBuilder对象,否则每次迭代都会创建新对象。创建对象需要系统资源,空间并且还需要时间。因此,从长远来看,请尽可能避免在循环中声明它们。



5

我建议为每次迭代创建一个新的StringBuffer(甚至更好的StringBuilder)。性能差异确实可以忽略不计,但是您的代码将更短,更简单。


2
如果您有任何这种性能差异的证据,请分享。(我也很高兴看到有关Java
最常

1
众所周知,分配(Java中的new关键字)比仅更改同一对象的某些字段要贵。对于“大部分”,您可以高度替换,有超过15亿的Android设备,全部使用Java。
路易CAD

1
我同意在某些情况下,代码的可读性比性能更重要,但是在这种情况下,它仅是一行代码!您可以运行一个基准测试,该基准测试将证明分配和对象创建以及垃圾回收比完全不进行更昂贵。当我们处于一个循环中时,它不仅仅具有相关性。
路易CAD

1
我也赞成Knuth的观点,但是他的观点并不总是正确的。增加一行以潜在地获得CPU周期和内存绝对不是坏事。您认为这个想法太直率了。请注意,循环通常重复很多次。循环可以重复数千次,如果不仔细进行优化,则可能在移动设备(以及服务器或计算机)上吞噬宝贵的兆字节。
路易CAD

1
我认为我们两个人都足够清楚地表达了我们的观点,并且我认为进一步的讨论不会对本评论部分有所帮助。如果您认为我的答案是错误,误导或不完整的,那么您(或任何人)都欢迎您对其进行编辑和/或下注。
伊莱·阿彻坎

5
public void clear(StringBuilder s) {
    s.setLength(0);
}

用法:

StringBuilder v = new StringBuilder();
clear(v);

为了提高可读性,我认为这是最好的解决方案。


2

那里已经很好的答案。只需为StringBuffer和StringBuild性能差异添加基准测试结果,即可在循环中使用新实例或在循环中使用setLength(0)。

摘要是:大循环

  • StringBuilder比StringBuffer快得多
  • 在循环中创建新的StringBuilder实例与setLength(0)没有区别。(setLength(0)比创建新实例具有非常非常小的优势。)
  • 通过循环创建新实例,StringBuffer比StringBuilder慢
  • StringBuffer的setLength(0)比在循环中创建新实例要慢得多。

非常简单的基准测试(我只是手动更改了代码并进行了不同的测试):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

循环中的新StringBuilder实例:时间成本:3693、3862、3624、3742

StringBuilder setLength:时间成本:3465、3421、3557、3408

循环中的新StringBuffer实例:时间成本:8327、8324、8284

StringBuffer setLength时间成本:22878、23017、22894

再次使用StringBuilder setLength来确保不是我的Labtop在StringBuffer setLength中使用这么长的时间有问题:-)时间花费:3448


0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

我认为这段代码更快。


3
这将出现性能问题。每次迭代后都会创建一个新对象
Aditya Singh 2015年

@AdityaSingh这是一种非常合理的方法。没有证据就不要承担性能问题。
shmosel

基于对正在发生的事情的理解来假设事物是智能的基础。
rghome
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.