簡介
總所周知,StringBuffer 是線程安全的,是 JDK 1.0 加入的;StringBuilder 是線程不安全的,是 JDK 1.5 加入的。
String & StringBuffer & StringBuilder 類的定義
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
①、三者都實現(xiàn)了 CharSequence 接口。也就是說,CharSequence 定義了字符串操作的接口,三者完成具體的實現(xiàn);
②、Serializable 是可以序列化的標(biāo)志;
③、后兩者繼承了 AbstractStringBuilder 類,而這個類封裝了 StringBuilder 和
StringBuffer 大部分操作的實現(xiàn)。
AbstractStringBuilder
AbstractStringBuilder 是 StringBuffer & StringBuilder 兩者的父類。
相同點:兩者的構(gòu)造函數(shù)都是調(diào)用了其父類的構(gòu)造函數(shù);
不同點:StringBuffer 大部分重寫了其父類的方法,并加同步鎖(synchronized),速度較慢;而 StringBuilder 則是直接調(diào)用其父類的方法,速度更快。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count; // value 數(shù)組中實際上存放的字符數(shù)目
AbstractStringBuilder() {}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
public int length() {
return count; //返回的是實際存放的字符數(shù)目
}
public int capacity() {
return value.length; //返回的是內(nèi)置字符數(shù)組的長度
}
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
private void ensureCapacityInternal(int minimumCapacity) {
// 如果需要擴(kuò)展到的容量 > 當(dāng)前字符數(shù)組長度,那么就正常擴(kuò)容
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
//用于保證字符數(shù)組長度的方法,ArrayList 中也使用這種動態(tài)擴(kuò)容的思想
void expandCapacity(int minimumCapacity) {
// 初始化新的容量大小 = 當(dāng)前字符串長度的 2 倍加 2
int newCapacity = value.length * 2 + 2;
// 新容量大小 < 傳進(jìn)來的最小容量,就用最小的容量作為新數(shù)組的容量
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
// 如果新的容量和最小容量都小于0
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
// 把容量設(shè)為Integer.MAX_VALUE
newCapacity = Integer.MAX_VALUE;
}
// 創(chuàng)建容量大小為 newCapacity 的新數(shù)組
value = Arrays.copyOf(value, newCapacity);
}
// 去除 value 字符數(shù)組中所有為空的元素,使其 count = value.length
public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}
...
}
屬性
由 AbstractStringBuilder 類可知,StringBuffer & StringBuilder 父類中封裝的字符數(shù)組沒有 final 修飾,也就說明了 StringBuffer & StringBuilder 中的字符數(shù)組可以被不斷修改。而 String 則相反。
方法
由于 StringBuffer & StringBuilder 大部分的方法都是直接調(diào)用或者重寫父類的方法,所以下面我們著重分析其父類 AbstractStringBuilder 的方法
構(gòu)造方法
StringBuffer & StringBuilder 兩者的構(gòu)造方法是一樣的,都需要調(diào)用父類的構(gòu)造方法。存儲的字符數(shù)組,也是在父類中定義的。
public StringBuilder() {
super(16); // 默認(rèn)的容量的大小是16。
}
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);
}
append()
public AbstractStringBuilder append(String str) {
// 如果傳入的參數(shù)為 null,則直接調(diào)用 appendNull() 方法在后面追加 'n'、'u'、'l'、'l' 四個字符。
if (str == null)
return appendNull();
int len = str.length();
// 首先,調(diào)用動態(tài)擴(kuò)容方法
ensureCapacityInternal(count + len);
// 將 str 從 0 到 len-1 位置上的字符復(fù)制到字符數(shù)組 value 中,并從 count
處開始存放(將 str 追加到 value 末尾)
str.getChars(0, len, value, count);
count += len;
// 返回對象本身,使其 append() 可以連續(xù)調(diào)用
return this;
}
delete()
刪除 [start,end) 區(qū)域的字符(包括 start,不包括 end)
public AbstractStringBuilder delete(int start, int end) {
// 健壯性的檢查
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
// 需要刪除的長度
int len = end - start;
if (len > 0) {
// 核心語句:將下標(biāo) [start,end] 區(qū)域的位置,用 [end,count] 區(qū)域的字符進(jìn)行覆蓋。
System.arraycopy(value, start+len, value, start, count-end);
// 并更新 count,這樣就只能輸出[0,count] 區(qū)域的字符。
count -= len;
}
return this;
}
delete 的底層操作,并沒有真正的刪除字符,而是把后面的字符進(jìn)行前移,從而覆蓋。其中 deleteCharAt 也是一樣的原理。
insert()
在 offset 位置插入字符串 str
public AbstractStringBuilder insert(int offset, String str) {
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
if (str == null)
str = "null";
int len = str.length();
// 調(diào)用擴(kuò)容方法
ensureCapacityInternal(count + len);
// 將 [offset,count] 區(qū)域字符串向后移動 len 個位置,為插入字符串留出空間
System.arraycopy(value, offset, value, offset + len, count - offset);
// 將 str 復(fù)制到 value 字符數(shù)組中 offset 之后的位置
str.getChars(value, offset);
// 更新當(dāng)前對象中記錄的長度
count += len;
return this;
}
該 insert 方法有很多重載,但是本質(zhì)上都離不開我們上述介紹的這個方法。
replace()
將 [start,end) 區(qū)域的字符串替換成 str
public AbstractStringBuilder replace(int start, int end, String str) {
// 健壯性的檢查
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
// 獲取需要添加的字符串的長度
int len = str.length();
// 計算新字符串的長度
int newCount = count + len - (end - start);
// 調(diào)用擴(kuò)容方法
ensureCapacityInternal(newCount);
// 將從 end 開始到最后的字符,向后移動到 start+len 的位置
System.arraycopy(value, end, value, start + len, count - end);
// 將 str 復(fù)制到 value 字符數(shù)組中 start 后面
str.getChars(value, start);
// 更新字符串長度
count = newCount;
return this;
}
toString()
返回了一個新的 String 對象,與原來的對象不共享內(nèi)存
public String toString() {
return new String(value, 0, count);
}
擴(kuò)展
①、在編譯階段就能夠確定的字符串常量,完全沒有必要創(chuàng)建String或StringBuffer對象。直接使用字符串常量的"+"連接操作效率最高;
②、StringBuffer & StringBuilder 對象的 append 效率要高于String 對象的"+"連接操作;
③、在不考慮線程安全的情況下,首選 StringBuilder 類,效率最高。否則選擇 StringBuffer。