StringBuilder的容量capacity变化,是每次2倍增长吗?(jdk1.8)
测试代码 1
StringBuilder sb =new StringBuilder();
System.out.println(sb.capacity() +"----" + sb.length());
sb.append("0000_0000_0000_0");
System.out.println(sb.capacity() +"----" + sb.length());
sb.append("000");
System.out.println(sb.capacity() +"----" + sb.length());
sb.append("0000_0000_0000_0");
System.out.println(sb.capacity() +"----" + sb.length());
sb.append("0000_0000_0000_0");
System.out.println(sb.capacity() +"----" + sb.length());
运行结果 1
16----016----1634----1970----3570----51
测试代码 2
StringBuilder sb =new StringBuilder();
sb.append("0000_0000_0000_0");
System.out.println(sb.capacity() +"----" + sb.length());
sb.append("0000_0000_0000_0000");
System.out.println(sb.capacity() +"----" + sb.length());
运行结果 2
16----1635----35
问题
从运行结果1和运行结果2都能看出,StringBuilder的容量变化规律貌似不再是网上很多人所说的那样(容量默认为16,追加数据时,如果容量不够,则每次2倍整地扩容,直到容量不小于StringBuilder的长度值为止。)
那容量的变化规律到底是什么样的呢?接下来进入源码进行分析。
测试代码 1 的源码分析
publicStringBuilder() {super(16);
}// StringBuilder的父类AbstractStringBuilderabstract class AbstractStringBuilder implements Appendable, CharSequence {/**
* 该变量用于存储字符(可见StringBuilder底层用的是字符数组实现的)
* The value is used for character storage.
*/char[] value;/**
* 已使用的字符个数
* The count is the number of characters used.
*/int count;// ...
AbstractStringBuilder(int capacity) {
value =newchar[capacity];
}// ...
}
- 先进入StringBuilder的构造方法,其又调用了父类的构造,初始化容量capacity依然还是16。这就是为何运行结果1中第一组数据中容量capacity为16的原因。
sb.append("0000_0000_0000_0");
执行后因为长度还是16,没有超过当前容量(capacity = 16)的范围,所以运行结果还是不变。sb.append("000");
执行后,容量capacity突然变为34,竟然不是2*16=32,why?容量因为这句代码的执行发生了改变,那么容量的变化规则一定在append方法之中。进入append方法的源码发现:ensureCapacityInternal(count + len);
这句代码就是重点,用于计算容量的大小的。
public AbstractStringBuilderappend(String str) {if (str ==null)return appendNull();int len = str.length();//调用该方法,确定最终容量Capacity
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;returnthis;
}
- 既然找到了方法,那就看看容量的具体变化规则吧:
privatevoidensureCapacityInternal(int minimumCapacity) {// overflow-conscious codeif (minimumCapacity - value.length >0)
expandCapacity(minimumCapacity);
}
其中minimumCapacity即为(count + len)的值,即 (已使用的字符个数 + 追加的新串的长度)的和(16 + 3 = 19),暂且叫最小容量值。
void expandCapacity(int minimumCapacity) {// 容量的变化规则在此int newCapacity = value.length *2 +2;if (newCapacity - minimumCapacity <0)
newCapacity = minimumCapacity;if (newCapacity <0) {if (minimumCapacity <0)// overflowthrownew OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
起初value.length为容量的默认值16,因newCapacity = value.length * 2 + 2;
所以有newCapacity = 34
即追加后的新容量为34。
容量的变化规律是2倍+2?
从测试代码1看起来下面的测试代码也能证明貌似是这样的,但是测试代码2的运行结果如何解释呢?,看源码,下面还有一句:
if (newCapacity - minimumCapacity <0)
newCapacity = minimumCapacity;
这句代码的意思是,如果原容量的2倍再加2的结果还小于最小容量值minimumCapacity的话,新的容量将按照最小容量值minimumCapacity来计算。此刻一切都明白了。
总结
StringBuilder的容量Capacity变化规则为:
- 初始值为16;
- 如果minimumCapacity(新添加的数据长度和已有数据长度的和)小于当前容量Capacity,则添加数据后容量Capacity不变;
- 如果minimumCapacity(新添加的数据长度和已有数据长度的和)大于当前容量Capacity,但minimumCapacity**小于**当前容量Capacity的2倍加2,则添加数据后容量Capacity变为上次容量的2倍加2;
- 如果minimumCapacity(新添加的数据长度和已有数据长度的和)大于当前容量Capacity的2倍加2,则添加数据后容量Capacity即为minimumCapacity(新添加的数据长度和已有数据长度的和)。
初始值 | 原容量 < 总内容 <= 2 * 原容量 + 2 | 总内容 > 2 * 原容量 + 2 |
---|---|---|
16 | 2 * 原容量 + 2 | 内容总长度 |