Java StringBuilder-重置或创建新的

Java StringBuilder-重置或创建新的,java,stringbuilder,Java,Stringbuilder,我有一个条件,StringBuilder不断存储与大型平面文件(100 MB)中的模式匹配的行。但是,在达到某个条件后,我将StringBuilder变量的内容写入文本文件 现在我想知道是否应该通过重置对象来使用相同的变量-> stringBuilder.delete(0,stringBuilder.length()) 或 就性能和OOM问题而言,请建议您认为哪一个更好。我认为StringBuilder#delete(start,end)仍然很昂贵,您应该: stringBuilder.set

我有一个条件,StringBuilder不断存储与大型平面文件(100 MB)中的模式匹配的行。但是,在达到某个条件后,我将StringBuilder变量的内容写入文本文件

现在我想知道是否应该通过重置对象来使用相同的变量->

stringBuilder.delete(0,stringBuilder.length())


就性能和OOM问题而言,请建议您认为哪一个更好。

我认为
StringBuilder#delete(start,end)
仍然很昂贵,您应该:

stringBuilder.setLength(0);
重新设置它


更新:查看后,似乎
setLength(int)
保留了旧的缓冲区,最好在上述调用之后调用:
StringBuilder#trimToSize()
尝试减少用于字符序列的存储空间

所以像这样的事情会更有效率:

stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer

这两者之间有更大的区别。第一个将保留删除字符之前的所有容量(即
stringBuilder.capacity()
),而第二个将使用默认容量创建新的
stringBuilder
。当然,您可以将
stringBuilder.capacity()
作为参数传递给构造函数,但是理解这里的区别很重要


在任何情况下,我都非常怀疑您是否会看到这两个变体之间存在实质性的性能差异,因此请选择可读性更好、更易于管理的变体。只有当您最终确定这会造成某种瓶颈时,才应该改变您的方法。

Imho,我建议您使用新的:

stringBuilder = new StringBuilder();
我从来没有听说过StringBuilder中存在内存泄漏,但当您真正达到极限时,您永远不会知道。我会通过每次使用一个新实例来对冲我的赌注

在最坏的情况下,你可能会失去一些效率,gc得到锻炼,但你排除了OOM的可能性

正因为如此,而且出于明确的原因,我个人会采用新方法。

我会使用:

 stringBuilder = new StringBuilder();
因为如果用大量数据填充,调用
stringBuilder.setLength(0)
不会取消分配备份阵列,因此您可能会看到内存使用率不必要地保持高水平


此外,它更易于阅读和理解。

如果您处于一个紧密的循环中,并且在将数据写入文件后将继续回到该循环中,您肯定应该重新使用StringBuilder。没有理由不这样做,这比搅动GC要好。如果你用C或C++写这个,你会重新使用缓冲区。 此外,虽然delete(…)方法调用System.arraycopy是真的,但复制的字节数是0,因此不重要


啊-有人提到我有一个setLength(…)方法,它是重新使用缓冲区的最快方法。

一个基本区别是sb.delete保留引用,而构造函数丢失它


如果SB是一个方法参数,并且应该用于将内容传递回调用方,则必须使用SB.delete。调用者持有原始引用。

理想情况下,我们应该使用
新的StringBuilder()
从StringBuilder类中挖掘一点,我了解了以下内容

创建新对象:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }
public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }
new StringBuilder()创建具有初始容量字符数组的新对象。 此处的开销:将调用GC以清除旧对象

使用删除:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }
public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }
public AbstractStringBuilder删除(int开始,int结束){
如果(开始<0)
抛出新StringIndexOutOfBoundsException(开始);
如果(结束>计数)
结束=计数;
如果(开始>结束)
抛出新的StringIndexOutOfBoundsException();
int len=结束-开始;
如果(len>0){
System.arraycopy(值,开始+长度,值,开始,计数结束);
计数-=len;
}
归还这个;
}
使用长度和修剪大小:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
    }
public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }
public void trimToSize(){
如果(计数<值.长度){
value=Arrays.copyOf(值,计数);
}
}
将从数组类调用copyOf

public static char[]copyOf(char[]original,int newLength){ char[]copy=new char[newLength]; 系统阵列副本(原件、0、副本、0、, Math.min(original.length,newLength)); 返回副本; }

现在它还将调用System.arrayCopy,这是一种本机方法。 现在,如果您看到在copyOf中,我们再次创建了一个长度为0的新字符, 当我们尝试再次向其添加数据时,它将调用expand,因为当前长度将为0。 因此,我认为最好调用新的StringBuilder()

您可以在上看到上面的代码


PS:@user3241961是write,如果您正在使用此对象的引用,则new需要重新设置它

重用创建的对象比始终分配新对象更便宜。它还避免了垃圾收集器的额外工作,因为您只处理一个对象

更快的方法是:

stringBuilder.setLength(0);

作为参考,
delete()
call执行一个
系统。arraycopy
。您应该根据您的应用程序工作流进行基准测试,并选择最适合您的应用程序。@SotiriosDelimanolis但在本例中,arraycopy的长度为0。我将分析这两个选项,以测量内存和cpu使用情况的性能,并获得特定应用程序的最终答案案例不过,在我看来,最好的选择是
stringBuilder=newstringbuilder()
并让GC完成它的工作。使用此技术可能会泄漏内存。如果创建越来越大的字符串,然后再创建越来越小的字符串,那么即使没有全部使用,底层的
char[]
也会越来越大。@anubhava我不是说性能。
setLength()
方法