3

在以下代码中...

StringBuffer buf = new StringBuffer("Is is a far, far better thing that i do");
System.out.println("buf = "+ buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());

buf.setLength(60);
System.out.println("buf = "+ buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());

buf.setLength(30);
System.out.println("buf = "+ buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());

...输出是:

buf = Is is a far, far better thing that i do 
buf.length() = 39
buf.capacity() = 55
buf = Is is a far, far better thing that i do
buf.length() = 60
buf.capacity() = 112
buf = Is is a far, far better thing 
buf.length() = 30
buf.capacity() = 112
4

5 回答 5

2

StringBuffer在几个点调用方法expandCapacity。如果它不会超大容量,则每次更改Stringbuffers 值时都必须分配一个新数组。所以这是某种性能优化。

从手册:

确保容量

公共无效确保容量(int minimumCapacity)

确保容量至少等于指定的最小值。如果当前容量小于参数,则分配具有更大容量的新内部数组。新容量是以下两者中的较大者:

* The minimumCapacity argument.
* Twice the old capacity, plus 2. 

如果 minimumCapacity 参数为非正数,则此方法不执行任何操作并简单地返回。

参数: minimumCapacity - 所需的最小容量。

于 2012-03-21T10:36:57.077 回答
2

调用setLength(60)将导致ensureCapacity(60)被调用1

ensureCapacity依赖于“阵列加倍”,这意味着每次需要增加时它都会(至少)加倍容量。准确的定义记录在Java Doc 中ensureCapacity

确保容量至少等于指定的最小值。如果当前容量小于参数,则分配具有更大容量的新内部数组。新容量是以下两者中的较大者:

  • minimumCapacity 参数。
  • 旧容量的两倍,加上 2。

如果 minimumCapacity 参数为非正数,则此方法不执行任何操作并简单地返回。

在您的特定情况下,第二个表达式(粗体)大于请求的容量,因此将使用它。由于 2*55 + 2 等于 112,这就是新容量。

相关问题:

1) 实际上,它会调用extendCapacity但其行为与确保容量相同。

于 2012-03-21T10:34:14.153 回答
2

考虑一下通常如何使用 StringBuffer。当我们需要存储在 StringBuffer 中的 String 超过当前容量时,当前容量就会增加。如果算法只是将容量增加到所需的数量,那么 StringBuffer 将非常低效。例如:

 buf.append(someText);
 buf.append(someMoreText);
 buf.append(Another100Chars);

可能需要将容量连续增加 3 倍。每次增加容量时,底层数据结构(数组)都需要在内存中重新分配,这涉及从堆中分配更多的 RAM,复制现有数据,然后最终对之前分配的内存进行垃圾回收。为了减少这种情况发生的频率,StringBuffer 将在需要时将其容量加倍。该算法将容量从 n 移动到 2n+2。这是实现此方法的 AbstraceStringBuilder 的源代码:

/**
 * This implements the expansion semantics of ensureCapacity with no
 * size check or synchronization.
 */
void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    if (newCapacity - minimumCapacity < 0)
        newCapacity = minimumCapacity;
    if (newCapacity < 0) {
        if (minimumCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
    value = Arrays.copyOf(value, newCapacity);
}

每次追加到 StringBuffer 或调用 setLength 时,都会调用此方法:

public synchronized void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > value.length) {
        expandCapacity(minimumCapacity);
    }
}
于 2012-03-21T10:49:49.713 回答
1

这是一个“阅读免费手册”的案例。来自 StringBuffer 的 Javadoc -

公共字符串缓冲区(字符串 str)

构造一个字符串缓冲区,初始化为指定字符串的内容。字符串缓冲区的初始容量是 16 加上字符串参数的长度。

这就解释了为什么它最初是 55。然后

公共无效确保容量(int minimumCapacity)

确保容量至少等于指定的最小值。如果当前容量小于参数,则分配具有更大容量的新内部数组。新容量是以下两者中的较大者:

• minimumCapacity 参数。

•旧容量的两倍,加上2。

如果 minimumCapacity 参数为非正数,则此方法不执行任何操作并简单地返回。

解释了为什么它变为 112。

于 2012-03-21T10:34:44.467 回答
0
public synchronized void setLength(int newLength) {
    super.setLength(newLength);
}

在超级:

public void setLength(int newLength) {
    if (newLength < 0)
        throw new StringIndexOutOfBoundsException(newLength);
    ensureCapacityInternal(newLength);
....

然后:

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
....

最后:

void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
....
于 2012-03-21T10:35:58.033 回答