StringBuilder是一個可變字符序列 (字符串)。和String相比,兩者都是字符串,都是字符的有序排列;不同的是,String是不可變的,而StringBuilder是可變的。
StringBuilder和StringBuffer幾乎一樣,只是前者沒有同步處理,更加高效。因此,這兩者的操作邏輯幾乎是一樣的,可以通過一個抽象的父類AbstractStringBuilder來實現共同的方法,而只需要在StringBuffer的方法中加上synchronized關鍵字即可。這點可以在源碼中明顯感覺到。
所以這裏就不討論StringBuffer了,只看StringBuilder,下面將結合AbstractStringBuilder一起來討論StringBuilder的應用。
StringBuffer主要就是幾個修改字符串的方法:append(),insert(),delete()。
有序字符串
StringBuilder和String一樣都是字符串,所以用同樣的方式來存儲字符:char數組。也是使用value和count屬性來表示:
char[]value;
int count;
StringBuilder默認新建的長度爲16,如果有初始化的String,就再加16,相當於預留16的可變長度。
public StringBuilder() {
super(16);
}
public StringBuilder(String str) {
super(str.length() +16);
append(str);
}
這裏調用了AbstractStringBuilder的構造函數,相當於初始化了一個長度爲16的char數組,然後調用append來把初始化的String賦值進去。
AbstractStringBuilder(int capacity) {
value =newchar[capacity];
}
append
接下來,我們看看StringBuilder.append(),這裏添加完之後,返回自身this,這樣可以連續的調用,很方便,如【sb.append("Hello").append("World")】
public StringBuilder append(Stringstr) {
super.append(str);
returnthis;
}
這裏實質上調用了AbstractStringBuilder.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;
returnthis;
}
public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin) {
// ……此處省略String的範圍判斷
System.arraycopy(value,offset + srcBegin, dst,dstBegin,srcEnd - srcBegin);
}
從上面可以看出來,append()是在char數組的末尾添加:首先校驗value的長度;然後再把String的char數組追加到value後面。
通過ensureCapacityInternal()校驗value的長度,溢出則調用expandCapacity()來擴展。 擴展邏輯:每次擴充原來產度的(len+1)*2,上限爲Integer.MAX_VALUE,下限爲追加後長度minimumCapacity。
可以看出如果超出這個大小,就會重新分配內存,創建一個更大的數組,並將原先的數組複製過來,再丟棄舊的數組。這個操作是比較耗性能的,要儘量避免,所以在創建StringBufferd時候要指定好容量,多預留一些空間。 同理Collection
void expandCapacity(int minimumCapacity) {
int newCapacity =value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
thrownew OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity); // 往後擴充數組
}
public staticchar[] copyOf(char[] original,int newLength) {
char[] copy =new char[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
上面討論完了append(String),這是最常用的情況。
如果只有一個字符的時候,不要用append(String),而要用append(char),邏輯更簡單高效,直接在後面追加一個字符即可,不用再依賴String的屬性。這也是FindBugs所提倡的用法。
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
returnthis;
}
insert
append()是在尾部追加,而想要中間插入的話,可以通過insert(),從下面代碼能看出來,和append()的邏輯不同的,只是在數組copy的時候,把value從offset處分開,留一個insertStr的長度的空位,把insertStr插入:【str.getChars(value,offset)】
public StringBuilder insert(int offset, Stringstr) {
super.insert(offset, str);
returnthis;
}
public AbstractStringBuilder insert(int offset, Stringstr) {
if ((offset < 0) || (offset > length()))
thrownew StringIndexOutOfBoundsException(offset);
if (str ==null)
str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
System.arraycopy(value, offset,value, offset + len,count - offset);
str.getChars(value, offset); //往空擋插入
count += len;
returnthis;
}
delete
使用delete()可以自由地刪除中間的字串。如下所示,實際上就是把end後的字串覆蓋到start的位置上,然後把count相應縮小,後面的沒有覆蓋的部分就自然無效了。
public StringBuilder delete(int start,int end) {
super.delete(start, end);
returnthis;
}
public AbstractStringBuilder delete(int start,int end) {
if (start < 0)
thrownew StringIndexOutOfBoundsException(start);
if (end >count)
end = count;
if (start > end)
thrownew StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) { // 把end後的字串覆蓋到start後
System.arraycopy(value, start+len,value, start, count-end);
count -= len; // 相應縮小count
}
returnthis;
}