admin管理员组

文章数量:1611548

    StringBuffer大家一定不陌生吧,今天浅谈一下StringBuffer中capacity的扩增机制。
    因为StringBuffer实际通过一个char[]引用来保存字符串的,它的长度是固定的。
如果append的值超过了数组容量,将会执行一个扩容方法,生成一个新数组,并将旧数组的值copy进去并替代。

一、capacity( ) 与length ( )

先来个小栗子: 
StringBuffer sb = new StringBuffer();
System.out.println(" this initial capacity: " + sb.capacity());
System.out.println(" this length: " + sb.length());
结果是这样的,我们来看StringBuffer的源码就不难理解了。
 this initial capacity: 16
 this length: 0
StringBuffer构造函数:
/**
 * Constructs a string buffer with no characters in it and an
 * initial capacity of 16 characters.
 */
public StringBuffer() {
    super(16);
}
StringBUffer实现的AbstractStringBuilder接口的构造函数:
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

二、StringBuffer的append

StringBuffer sb1 = new StringBuffer(" Hello World!!! ");
System.out.println(" this capacity: " + sb1.capacity());
System.out.println(" this length: " + sb1.length());

StringBuffer sb2 = new StringBuffer();
sb2.append(" Hello World!!! ");
System.out.println(" this capacity: " + sb2.capacity());
System.out.println(" this length: " + sb2.length());

这里打印出来应该是多少呢?结果让人出乎意料。

 this capacity: 32
 this length: 16
 this capacity: 16
 this length: 16

这是为什么呢?我们来看看带参构造函数:

public StringBuffer(String str) {
   super(str.length() + 16);
    append(str);
}

原来他会把传入的字符串再加16。。。

三、capacity的扩容机制

我简单的写了一个循环来往StringBuffer里面append字符串,并监测容量的变化。

StringBuffer sb1 = new StringBuffer();
for (int num = 0; num < 5; num++) {
    sb1.append("12345678");
    System.out.println(" this capacity: " + sb1.capacity());
    System.out.println(" this length: " + sb1.length());
    System.out.println("---------------------------------");
}

直接上结果:

 this capacity: 16
 this length: 8
---------------------------------
 this capacity: 16
 this length: 16
---------------------------------
 this capacity: 34
 this length: 24
---------------------------------
 this capacity: 34
 this length: 32
---------------------------------
 this capacity: 70
 this length: 40
---------------------------------

每次循环append了一个长度是8的字符串,他的容量变化:
16 - 16 - 34 - 34 - 70
结论:capacity增长的规律为 旧值*2+2
增长顺序怎么是这样的呢?
我们来深入源码:

public AbstractStringBuilder append(StringBuffer sb) {
    if (sb == null)
        return append("null");
    int len = sb.length();
    ensureCapacityInternal(count + len);
    sb.getChars(0, len, value, count);
    count += len;
    return this;
}

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
}
/**
 * This implements the expansion semantics of ensureCapacity with no
 * size check or synchronization.
 */
void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    //如果minimumCapacity大于原容量*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);
}

四、可大不可小的capacity

我们再来个例子,看看capacity的变化情况

StringBuffer sb1 = new StringBuffer();
System.out.println(" before ensure: " + sb1.capacity());

sb1.ensureCapacity(10);
System.out.println(" now capacity " + sb1.capacity() + " first ensureCapacity: " + sb1.capacity());

sb1.ensureCapacity(20);
System.out.println(" now capacity " + sb1.capacity() + " second ensureCapacity: " + sb1.capacity());

sb1.ensureCapacity(80);
System.out.println(" now capacity " + sb1.capacity() + " third ensureCapacity: " + sb1.capacity());
 before ensure: 16
 now capacity 16 first ensureCapacity: 16
 now capacity 34 second ensureCapacity: 34
 now capacity 80 third ensureCapacity: 80

当设置StringBuffer的容量
1、小于当前容量时,容量不变。
本例中,容量依然为16。
2、大于当前容量,并且小于(当前容量+1)*2,则容量变为(当前容量+1)*2。
本例中,16<20<(16+1)*2=34,所以容量为34。
3、大于当前容量,并且大于(当前容量+1)*2,则容量变为用户所设置的容量。
本例中,80>16,80>(16+1)*2=34,所以容量为80。

原因再源码中可以找到:

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

本文标签: 机制StringBufferCapacity