清空字符串缓冲区/构建器在循环后

139

如何在Java中清空字符串缓冲区,以便下一次迭代使用一个清空的字符串缓冲区?


2
另一个问题:为什么您仅在循环的一次迭代中使用SB?是否有另一个内部迭代?如果您只想执行A+B+C+D(Java编译器将在内部使用SB),则SB不值得使用。如果您想有条件地添加字符串的某些部分,则可以使用它,但否则...只需使用“+”即可。 - helios
10个回答

150

一个选项是使用以下的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了解更多信息:


16
更好的做法是在循环内部声明StringBuffer。 - Mark Elliot
12
啊,我认为sb.setLength(0)比在循环内部声明更加简洁高效。你的解决方案违背了使用StringBuffer的性能优势... - Jonathan Holloway
6
我认为性能的提升来自于字符串的可变性,而不是节省实例化。这是一个执行1亿次迭代的快速测试:循环内(2.97秒):http://ideone.com/uyyTL14w,循环外(2.87秒):http://ideone.com/F9lgsIxh。 - Mark Elliot
6
说到性能:除非您的代码在多线程情况下被访问,否则应该使用 StringBuilder 而不是 StringBuffer -- 参见 javadoc:“如果可能的话,建议优先使用本类而不是 StringBuffer,因为在大多数实现中它会更快。” - Rahel Lüthy
4
将SB对象放在循环外唯一的好处是不会丢失其内部(潜在的长)char[]。如果在第一个迭代器中它增长足够多,第二个循环将不需要调整大小任何char []。但要获得这种优势,“清除方法”必须保留内部数组的大小。setLength可以做到这一点,但它还会将SB中未使用的所有字符设置为\u0000,因此相对于仅创建具有良好初始容量的新SB,它的性能较低。声明在循环内部更好。 - helios
显示剩余6条评论

56

重复使用 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
当我看到setLength代码源时,它是如何工作的(https://gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1),这里count将始终大于newLength(0)吗? - Thomas Decaux

25

你有两个选择:

要么使用:

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对象,否则它将在每次迭代中创建新对象。创建对象需要系统资源和空间,并且也需要时间。因此,如果可能,长期来看应该避免在循环内部声明它们。


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

使用方法:

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

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


6

我建议为每个迭代创建一个新的StringBuffer(或者更好的是StringBuilder)。性能差异非常小,但您的代码会更短更简单。


2
如果您有任何关于这种性能差异的证据,请分享。 (我也很乐意看到Java主要用于哪些领域,以及“主要”定义下的情况统计数据。) - Eli Acherkan
1
众所周知,在Java中分配(new关键字)比仅更改同一对象的某些字段更昂贵。对于“大多数”情况,您可以将其替换为“高度”,因为有超过15亿台使用Java的Android设备。 - Louis CAD
1
我同意在某些情况下,代码的可读性比性能更重要,但在这种情况下,只有一行代码!而且您可以运行基准测试,证明分配和对象创建以及垃圾收集比根本不做更昂贵。由于我们处于循环中,这比较相关。 - Louis CAD
1
我也认同Knuth的观点,但他的观点并不总是正确的。添加仅一行代码以潜在地获得CPU周期和内存绝对不是邪恶的。你把这个想法想得太过简单了。请注意,循环通常会重复多次。如果没有仔细优化,循环可以重复数千次,在移动设备(以及服务器或计算机)上可能会消耗宝贵的兆字节。 - Louis CAD
1
我认为我们都已经清楚地表达了自己的观点,我感觉进一步讨论对于这个评论区并没有好处。如果你认为我的回答是不正确、误导或不完整的,那么你 - 或任何人 - 都可以编辑和/或给它点踩。 - Eli Acherkan
显示剩余5条评论

5
buf.delete(0,  buf.length());

2

已经有很好的答案了。只需添加一个基准测试结果,以比较StringBuffer和StringBuilder在循环中使用新实例或在循环中使用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方法以确保不是我的笔记本电脑出了一些问题,无法使用如此长的StringBuffer setLength方法 :-) 时间成本:3448


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

我觉得这段代码更快。


3
性能会有影响。每次迭代后都会创建一个新对象。 - Aditya Singh
@AdityaSingh 这是一个完全合理的方法。不要在没有证据的情况下假设性能问题。 - shmosel
1
基于对事情的理解而做出假设是智能的基础。 - rghome

0
我使用以下代码来存储密码,以进行临时处理,例如正则表达式匹配,并在完成后清除它。通常的删除方法无法重置所有字符,这对我来说不太合适。这段代码满足了我的要求。
    public static void clear(StringBuilder value) {
        for (int i = 0, len = value.length(); i < len; i++) {
            value.setCharAt(i, Character.MIN_VALUE);
        }
        value.setLength(0);
    }

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("clear this password");
        // use & process sb
        clear(sb);
    }

-3

我认为清空 StringBuilder 最好的方法是使用 Clear() 方法。
StringBuilder sb = new StringBuilder(); sb.Clear();

注意:这仅适用于 .net


1
该方法.Clear()不存在。请参阅Javadoc。 - Andrea Richiardi
这仅适用于 .net https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.clear?view=net-6.0 - M2012

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接