StringBuilder底层是一个char[ ] ,何以见得,上源码:
public final class StringBuilder
extends AbstractStringBuilder //继承了一个类
implements java.io.Serializable, CharSequence //实现了两个接口
{
……
…… //类的具体内容暂时省略掉
}
在继承的AbstractStringBuilder类中 可以很清楚的看到是一个char[ ] 数组,数组名是value(记住数组名哈,后面有用)
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value; //可以很清楚的看到是一个char[] 数组,数组名是value
/**
* The count is the number of characters used.
*/
int count;
………… //类的具体内容先省略掉
}
下面来详细说明一下几种不同情况添加元素的方式:
1、直接用无参构造器添加
添加时需要搭配append()方法
先上无参构造的源码:
public StringBuilder() {
super(16); //调用了父类的无参构造方法
}
父类的无参构造方法 在父类的无参构造方法中,将16传进来,new了一个长度为16的数组
AbstractStringBuilder(int capacity) {
value = new char[capacity]; //将数组长度设置为16
}
以上可以见得,通过无参构造进行添加元素,数组的初始长度为16。
通过调用append()添加元素时:
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
一种是添加任意类型的元素,后将其转为字符串。一种为直接添加字符串。两种方式最终的结果是一样的。
public AbstractStringBuilder append(String str) {
if (str == null) //当添加的字符串为空时
return appendNull(); //进入该方法
int len = str.length(); //不为空时,用len变量来承接字符串的长度
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
由上面源代码可分为两种情况。
(1)当添加的字符串为null时。
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
可以在源码上看到当str==null时,是将null转换为字符串添加进入value[ ]中。此时数组中的内容为"null"四个字符。
(2)当添加的字符串不为null时
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
将添加的字符串的长度+ 原数组元素的个数(此时为第一次添加,原数组元素个数为0,以下将字符串长度即说明为两者之和)和value[ ]的长度作比较,
①如果添加的字符串长度大于当前数组的长度(为初始化长度16)
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
进行数组长度扩容,如何扩容? value.length << 1 + 2
由以上代码可以看到,先将 数组长度左移1位,再+2。意思就是将初始的数组长度✖2 +2(即位34)。扩容后再与要添加的字符串长度相比较。
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
如果新扩容的数组长度仍小于要添加的字符串的长度。则直接将要添加的字符串长度直接当作数组新长度。
最后对这个新长度做个判断,如果该长度小于0或者该长度大于最大限制长度。抛出异常(此源代码未展示)。否则就返回该新长度。
执行:value = Arrays.copyOf(value,newCapacity(minimumCapacity));
copyOf()方法
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
即将添加的字符串逐个添加进数组元素中,并返回copyOf[ ] ,就完成了无参构造的添加元素操作啦。
②如果添加的字符串长度小于初始化数组的长度。(即小于16)。直接要添加的字符串添加入数组中。
2、通过含参构造器添加字符时。
public StringBuilder(int capacity) { //直接添加一个长度
super(capacity);
}
public StringBuilder(String str) { //添加一个字符串
super(str.length() + 16);
append(str);
}
public StringBuilder(CharSequence seq) { //采用多态形式添加
this(seq.length() + 16);
append(seq);
}
①直接添加一个长度
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
直接将该长度作为数组长度。
②添加一个字符串。 调用父类的含参构造。创建一个长度为字符串长度+16的一个数组
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
说明:通过含参构造添加字符串,数组的默认长度是:字符串的长度+16。
public StringBuilder(String str) { //添加一个字符串
super(str.length() + 16);
append(str);
}
此时要注意当添加的字符串为null时(即String str == null)。str.length()会直接报错。在此处和append()添加不一样。
然后执行添加操作append(str)。
③CharSequence seq 为一个接口。该方式可通过该接口的实现类进行传参。(多态性)
感兴趣的朋友们可以自己看一下该接口的源码。基本上和上述的原理相同。在此不过多论述。
总结:StringBuilder的底层是一个char [ ]。
通过无参构造,append()添加元素时。数组的默认长度为16,每次扩容为数组原长度的2倍+2(value.length<<1+2)。
通过含参构造添加元素时。
①直接添加一个长度。数组的初始长度为该长度
②添加一个字符串。数组的初始长度为该字符串的长度+16(str.length+16)。每次扩容为数组原长度的2倍+2(和上述append()方法相同,因为也是通过append()添加元素的)
看完留给一个代码小测验。看看自己的理解程度。
public static void main(String[] args) {
String str = null;
StringBuilder str1 = new StringBuilder();
str1.append(str);
System.out.println(str1.length());
StringBuilder str2 = new StringBuilder(str);
Sustem.out.println(str2);
}
结果为 :
4
java.lang.NullPointerException(报错)