StringBuilder與StringBuffer是兩個常用的操作字串的類別。大家都知道,StringBuilder是線程不安全的,而StringBuffer是線程安全的。前者是JDK1.5加入的,後者在JDK1.0就有了。下面分析一下它們的內部實作。
一、繼承關係
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence
可以看到,兩個類別的繼承關係是一模一樣的。 Serializable是可以序列化的標誌。 CharSequence介麵包含了charAt()、length() 、subSequence()、toString()這幾個方法,String類別也實作了這個介面。這裡的重點是抽象類別AbstractStringBuilder,這個類別封裝了StringBuilder和StringBuffer大部分運算的實作。
二、AbstractStringBuilder
1、變數及建構方法
char[] value; int count; AbstractStringBuilder() { } AbstractStringBuilder(int capacity) { value = new char[capacity]; }
AbstractStringBuilder內部用一個char[]陣列保存字串,可以在建構的時候指定初始容量方法。
2、擴容
public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } 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); }
擴容的方法最終是由expandCapacity()實現的,在這個方法中首先把容量擴大為原來的容量加2,如果此時仍小於指定的容量,那麼就把新的容量設為minimumCapacity。然後判斷是否溢出,如果溢出了,把容量設為Integer.MAX_VALUE。最後把value值進行拷貝,這顯然是耗時操作。
3、append()方法
public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
append()是最常用的方法,它有很多形式的重載。上面是其中一種,用於追加字串。如果str是null,則會呼叫appendNull()方法。這個方法其實是追加了'n'、'u'、'l'、'l'這幾個字元。如果不是null,則先擴容,然後呼叫String的getChars()方法將str追加到value末端。最後回傳物件本身,所以append()可以連續呼叫。
三、StringBuilder
AbstractStringBuilder已經實作了大部分需要的方法,StringBuilder和StringBuffer只需要呼叫即可。下面來看看StringBuilder的實作。
1、建構器
public StringBuilder() { super(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); }
可以看出,StringBuilder預設的容量大小為16。當然也可以指定初始容量,或以一個已有的字元序列為StringBuilder物件賦初始值。
2、append()方法
public StringBuilder append(String str) { super.append(str); return this; } public StringBuilder append(CharSequence s) { super.append(s); return this; }
append()的重載方法很多,這裡隨便列舉了兩個。顯然,這裡是直接呼叫的父類別AbstractStringBuilder中的方法。
3、toString()
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
toString()方法傳回了一個新的String對象,與原來的物件不共享記憶體。其實AbstractStringBuilder中的subString()方法也是如此。
四、SringBuffer
StiringBuffer跟StringBuilder類似,只不過為了實現同步,很多方法使用lSynchronized修飾,如下面的方法:
public synchronized int length() { return count; } public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } public synchronized void setLength(int newLength) { toStringCache = null; super.setLength(newLength); }
另外,在上面的append()以及setLength()方法裡面還有個變數toStringCache。這個變數是用於最近一次toString()方法的緩存,任何時候只要StringBuffer被修改了這個變數會被賦值為null。 StringBuffer的toString如下:
public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
String(char[] value, boolean share) { // assert share : "unshared not supported"; this.value = value; }