1、字符串“+”实际上也是调用的StringBulider的方法
2、在字符串拼接的时候尽量使用StringBulider而不是“+”,StringBulider性能更高。
具体原因如下:
String a="a";
a=a+"a";
a=a+"b";
上边的代码实际相当于
String a="a";
a=new StringBulider(a).append("a").toString();
a=new StringBulider(a).append("b").toString();
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
为了数据的安全性,不破坏数据的封装,StringBulider toString方法并不会直接返回StringBulider的char数组,而是使用了保护性拷贝,拷贝一个新的char[]数组并赋给一个新的字符串。
对比之后就不难理解为什么不要使用“+”了,因为会大量new StringBulider,并大量调用toString方法,不仅效率低而且占用内存。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
上边的代码是StringBulider的三个构造方法,我们都知道StringBulider是把数据存储在char数组中,而StringBulider默认的数组长度是16,那么当你调用append方法时候数组长度不够会怎么处理呢
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
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);
}
上边是append方法的处理过程,可以看到当数组长度不够的时候,先判断将长度放大两倍够不够,不够的话
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
使用原数组长度+要添加的字符串长度,之后使用Arrays.copyOf(value, newCapacity)方法新建数组并复制原数组。
假设要拼接的字符串长度分别为1,32,64,128会发生什么,StringBulider会不停的调用3次Arrays.copyOf(value, newCapacity)方法来拓展数组空间
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;
}
Arrays.copyOf方法并不会在原有的数组上增加长度,而是重新声明一个数组,并将原数组拷贝过去,效率低,而且浪费空间(因为原数组的空间将不被使用)。
当你将StringBulider初始化数组长度设置为64,128或者256呢,Arrays.copyOf(value, newCapacity)将会分别调用2,1,0次。
所以,合理设置一个初始值非常重要