StringBuilder 與 StringBuffer 是兩個經常使用的操做字符串的類。java
你們都知道, StringBuilder 是線程不安全的,而 StringBuffer 是線程安全的。前者是JDK1.5加入的,後者在JDK1.0就有了。在執行速度方面,StringBuilder > StringBuffer > String。數組
下面分析一下它們的內部實現。緩存
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 大部分操做的實現。安全
char[] value; int count; AbstractStringBuilder() { } AbstractStringBuilder(int capacity) { value = new char[capacity]; }
AbstractStringBuilder 內部用一個 char[] 數組保存字符串,能夠在構造的時候指定初始容量方法。app
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 值進行拷貝, 這顯然是耗時操做 。ui
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() 能夠連續調用。this
AbstractStringBuilder 已經實現了大部分須要的方法, StringBuilder 和 StringBuffer 只須要調用便可。下面來看看 StringBuilder 的實現。spa
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 對象賦初始值。線程
public StringBuilder append(String str) { super.append(str); return this; } public StringBuilder append(CharSequence s) { super.append(s); return this; }
append() 的重載方法不少,這裏隨便列舉了兩個。顯然,這裏是直接調用的父類 AbstractStringBuilder 中的方法。code
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
toString() 方法返回了一個新的 String 對象,與原來的對象不共享內存。其實 AbstractStringBuilder 中的 subString() 方法也是如此。
StiringBuffer 跟 StringBuilder 相似,只不過爲了實現同步,不少方法使用l Synchronized 修飾,以下面的方法:
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); }
能夠看到,方法前面確實加了 Synchronized 。
另外,在上面的 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); }
在這個方法中,若是 toStringCache 爲 null 則先緩存。最終返回的 String 對象有點不一樣,這個構造方法還有個參數 true 。找到 String 的源碼看一下:
String(char[] value, boolean share) { // assert share : "unshared not supported"; this.value = value; }
原來這個構造方法構造出來的 String 對象並無實際複製字符串,只是把 value 指向了構造參數,這是爲了節省複製元素的時間。不過這個構造器是具備包訪問權限,通常狀況下是不能調用的。
StringBuilder 和 StringBuffer 都是可變字符串,前者線程不安全,後者線程安全。
StringBuilder 和 StringBuffer 的大部分方法均調用父類 AbstractStringBuilder 的實現。其擴容機制首先是把容量變爲原來容量的2倍加2。最大容量是 Integer.MAX_VALUE ,也就是 0x7fffffff 。
StringBuilder 和 StringBuffer 的默認容量都是16,最好預先估計好字符串的大小避免擴容帶來的時間消耗。