3

In the following code...

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());

... the output is:

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
aioobe
  • 413,195
  • 112
  • 811
  • 826
CP3O
  • 429
  • 1
  • 6
  • 21

5 Answers5

2

A call to setLength(60) will cause ensureCapacity(60) to be called1.

ensureCapacity relies on "array doubling" which means that it will (at least) double the capacity each time it needs to be increased. The precise definition is documented in the Java Doc for ensureCapacity:

Ensures that the capacity is at least equal to the specified minimum. If the current capacity is less than the argument, then a new internal array is allocated with greater capacity. The new capacity is the larger of:

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

If the minimumCapacity argument is nonpositive, this method takes no action and simply returns.

In your particular case, the second expression (in bold) is larger than the requested capacity, so this will be used. Since 2*55 + 2 equals 112, that's what the new capacity will be.

Related question:

1) Actually, it will call extendCapacity but that behaves the same as ensure capacity.

Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
2

StringBuffer calls at several points the method expandCapacity. If it wouldn't oversize the capacity, it would have to allocate a new array, everytime you changes the Stringbuffers value. So this is some kind of performance optimization.

From the manual:

ensureCapacity

public void ensureCapacity(int minimumCapacity)

Ensures that the capacity is at least equal to the specified minimum. If the current capacity is less than the argument, then a new internal array is allocated with greater capacity. The new capacity is the larger of:

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

If the minimumCapacity argument is nonpositive, this method takes no action and simply returns.

Parameters: minimumCapacity - the minimum desired capacity.

Christian Strempfer
  • 7,291
  • 6
  • 50
  • 75
2

Consider how StringBuffer is typically used. When the String we need to store in a StringBuffer exceeds the current capacity, the current capacity is increased. If the algorithm only increased the capacity to the required amount, then StringBuffer would be very inefficient. For example:

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

might require that the capacity be increased three times in a row. Every time the capacity is increased, the underlying data structure (an array) needs to be re-allocated in memory, which involves allocating more RAM from the heap, copying the existing data, and then eventually garbage collecting the previously allocated memory. To reduce the frequency of this happening, StringBuffer will double its capacity when needed. The algorithm moves the capacity from n to 2n+2. Here is the source code from AbstraceStringBuilder where this method is implemented:

/**
 * 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);
}

Every time you append to a StringBuffer or call setLength, this method is called:

public synchronized void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > value.length) {
        expandCapacity(minimumCapacity);
    }
}
Thorn
  • 4,015
  • 4
  • 23
  • 42
1

This is a case of "read the free manual". From the Javadoc for StringBuffer -

public StringBuffer(String str)

Constructs a string buffer initialized to the contents of the specified string. The initial capacity of the string buffer is 16 plus the length of the string argument.

which explains why it's initially 55. Then

public void ensureCapacity(int minimumCapacity)

Ensures that the capacity is at least equal to the specified minimum. If the current capacity is less than the argument, then a new internal array is allocated with greater capacity. The new capacity is the larger of:

•The minimumCapacity argument.

•Twice the old capacity, plus 2.

If the minimumCapacity argument is nonpositive, this method takes no action and simply returns.

explains why it changes to 112.

Community
  • 1
  • 1
Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
0
public synchronized void setLength(int newLength) {
    super.setLength(newLength);
}

in super:

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

Then:

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

And finally:

void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
....
assylias
  • 321,522
  • 82
  • 660
  • 783