26

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

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

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

或者

stringBuilder=new StringBuilder();

请就性能和OOM问题提出您认为哪个更好。

4

8 回答 8

38

我认为StringBuilder#delete(start, end)仍然是昂贵的电话,你应该这样做:

stringBuilder.setLength(0);

重置它。


更新:查看源代码后StringBuilder,旧缓冲区似乎setLength(int)完好无损,最好调用:StringBuilder#trimToSize()在上面调用 which 之后attempts to reduce storage used for the character sequence

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

stringBuilder.setLength(0); // set length of buffer to 0
stringBuilder.trimToSize(); // trim the underlying buffer
于 2013-09-12T14:22:44.830 回答
6

恕我直言,我建议使用新的:

stringBuilder = new StringBuilder();

我从未听说过 StringBuilder 中的内存泄漏,但是当您真正突破极限时,您永远不知道。我每次都会使用一个新实例来对冲我的赌注。

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

正因为如此,而且为了清楚起见,我个人会采用新方法。

于 2013-09-12T14:27:43.797 回答
5

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

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

于 2014-01-27T20:02:15.803 回答
3

好吧,两者之间的差异更大。第一个保留删除字符之前的任何容量(即stringBuilder.capacity()),而第二个StringBuilder使用默认容量 16 创建一个新容量。当然,您可以将stringBuilder.capacity()其作为参数传递给构造函数,但了解此处的区别很重要,尽管如此。

无论如何,我非常怀疑您会看到这两个变体之间的显着性能差异,因此请选择更具可读性和更易于管理的那个。只有当您最终确定这会导致某种瓶颈时,您才应该改变您的方法。

于 2013-09-12T14:24:57.020 回答
2

It is cheaper reuse the created object than allocate a new one, always. It also avoids extra work for the Garbage Collector, as you are handling only an object.

The faster way is:

stringBuilder.setLength(0);
于 2017-03-30T07:51:05.223 回答
1

我会使用:

 stringBuilder = new StringBuilder();

因为如果你用大量数据填充它,调用stringBuilder.setLength(0);不会取消分配后备数组,所以你可能会看到内存使用率保持在不必要的高位。

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

于 2013-09-12T14:30:26.987 回答
1

理想情况下,我们应该在grepcodenew StringBuilder() 的 StringBuilder 类中使用 Digging我了解以下内容。

创建新对象:

/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

new StringBuilder() 创建一个具有初始容量 char 数组的新对象。这里的开销:将调用 GC 来清除旧对象。

使用删除:

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;
    }

使用 Length 和 TrimToSize :

public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }

将从数组类调用 copyOf

public static char[] copyOf(char[] original, int newLength) { char[] copy = new char[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); 返回副本;}

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

你可以在grepcode上看到上面的代码

PS:@user3241961 是写的,以防您使用此对象的引用,那么 new 将需要再次设置它

于 2015-07-29T13:11:35.433 回答
0

如果您处于紧密循环中,并且在将数据写入文件后将继续返回该循环,则绝对应该重新使用 StringBuilder。没有理由不这样做,这比搅动 GC 更好。如果您使用 C 或 C++ 编写此代码,您将重新使用缓冲区。

此外,虽然 delete(...) 方法确实调用了 System.arraycopy,但复制的字节数为 0,因此它无关紧要。

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

于 2013-09-12T14:37:39.497 回答