Java StringBuilder和StringBuffer源碼分析
簡介
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
變量及構造方法
char[] value; int count; AbstractStringBuilder() { } AbstractStringBuilder(int capacity) { value = new char[capacity]; }
AbstractStringBuilder 內部用一個 char[] 數組保存字符串,可以在構造的時候指定初始容量方法。
擴容
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 值進行拷貝, 這顯然是耗時操作 。
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 的實現。
構造器
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 對象賦初始值。
append()方法
public StringBuilder append(String str) { super.append(str); return this; } public StringBuilder append(CharSequence s) { super.append(s); return this; }
append() 的重載方法很多,這里隨便列舉了兩個。顯然,這里是直接調用的父類 AbstractStringBuilder 中的方法。
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 類似,只不過為了實現同步,很多方法使用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,最好預先估計好字符串的大小避免擴容帶來的時間消耗。