We know that StringBuffer's default capacity is 16 and when we try to add 17th char it will be increased by following rule:
newCapacity = (current capacity + 1) *2;
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaa"); // length is 16
System.out.println(sb.capacity()); // it gives 16
If I add 17th char
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaa"); // length is 17
System.out.println(sb.capacity()); // it gives 34
But confusing part is now
If I try to add 35 chars
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // length is 35
System.out.println(sb.capacity()); // it gives 35
capacity should have been increased by 70 at this point of time.
Interesting part is
StringBuffer sb = new StringBuffer();
sb.append("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // length is 34
sb.append("a"); // added 35th char
System.out.println(sb.capacity()); // it gives 70 which is correct
Can any one shed some light on this ?
The specifics may depend slightly on JDK version, but on my local version of 1.8.0_66:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
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);
}
Note that value.length
is actually capacity, not the length of the string being stored. The number of characters currently in the buffer is count
! Also recall that value.length
is initially 16, when calling new StringBuffer()
. With those things in mind, let's do a little bit of stack tracing for each of the cases you presented.
For a string of size 17:
sb.append("12345678901234567")
if (str == null) -> false
len = 17;
ensureCapacityInternal(0 + 17)
if (17 - 16 > 0) -> true
expandCapacity(17)
newCapacity = 16 * 2 + 2 = 34
if (34 - 17 < 0) -> false
value = Arrays.copyOf("", 34)
str.getChars(0, 17, "", 17)
return this
sb.build() -> "12345678901234567"
sb.capacity() -> 34
For a string of size 35:
sb.append("12345678901234567890123456789012345")
if (str == null) -> false
len = 35;
ensureCapacityInternal(0 + 35)
if (35 - 16 > 0) -> true
expandCapacity(35)
newCapacity = 16 * 2 + 2 = 34
if (34 - 35 < 0) -> true
newCapacity = 35
value = Arrays.copyOf("", 35)
str.getChars(0, 35, "", 35)
return this
sb.build() -> "12345678901234567890123456789012345"
sb.capacity() -> 35
Note that the difference comes on the if (newCapacity - minimumCapacity < 0)
line. If a string is appended that is longer than oldCapacity * 2 + 2
, then newCapacity
will be set to the length of the string to be appended.
In other words, when appending, if the buffer is not big enough to hold the existing text plus the appended text, it will check if (roughly) doubling in size would hold the new text. If that is still not enough, rather than recursively expanding, it will expand to exactly big enough.
This doesn't only happen with 35, though with strings much longer than that you probably wouldn't be running into the case where what you're appending is more than twice as long as your current capacity.
You would also see the same "length = capacity" if you were to do, say
StringBuffer sBuffer = new StringBuffer();
sBuffer.append("1234567890123456");
System.out.println(sBuffer.capacity()); // 16
sBuffer.append("1234567890123456789");
System.out.println(sBuffer.capacity()); // 35
But not
StringBuffer sBuffer = new StringBuffer();
sBuffer.append("1234567890123456");
System.out.println(sBuffer.capacity()); // 16
sBuffer.append("123456789012345678");
System.out.println(sBuffer.capacity()); // 34
sBuffer.append("1");
System.out.println(sBuffer.capacity()); // 70