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,最好預先估計好字串的大小避免擴容帶來的時間消耗。
相關文章
- JAVA面試題 StringBuffer和StringBuilder的區別,從原始碼角度分析?Java面試題UI原始碼
- StringBuilder StringBuffer String的區別(原始碼分析)-javaUI原始碼Java
- Java StringBuffer 和 StringBuilder 類JavaUI
- Stringbuffer與Stringbuilder原始碼學習和對比UI原始碼
- 【Java】String、StringBuilder和StringBufferJavaUI
- java String,StringBuilder和StringBufferJavaUI
- [Java]String、StringBuilder和StringBufferJavaUI
- StringBuffer和StringBuilderUI
- Java 原始碼出發徹底搞懂String與StringBuffer和StringBuilder的區別Java原始碼UI
- StringBuffer 和 StringBuilder 類UI
- Java String StringBuilder 和 StringBuffer 用法詳解JavaUI
- String、StringBuilder和StringBufferUI
- stringbuilder和stringbuffer區別UI
- 從原始碼看String,StringBuffer,StringBuilder的區別原始碼UI
- java基礎學習之八:StringBuffer和StringBuilderJavaUI
- Java:字串緩衝區StringBuffer和StringBuilder的使用Java字串UI
- StringBuffer和StringBuilder的區別UI
- StringBuilder和StringBuffer的區別UI
- [java基礎]StringBuilder和StringBuffer的3個區別JavaUI
- Java字串(String_StringBuilder_StringBuffer)Java字串UI
- String和StringBuilder和StringBuffer三兄弟UI
- Java 21的StringBuilder和StringBuffer新增了一個repeat方法JavaUI
- Java中String,StringBuffer和StringBuilder的區別(轉載)JavaUI
- String、StringBuffer和StringBuilder類的UI
- C# string、stringBuffer和stringBuilderC#UI
- System,Runtime,Math,StringBuffer和StringBuilderUI
- java複習之 String,StringBuffer,StringBuilderJavaUI
- StringBuffer 和 StringBuilder 的 3 個區別UI
- Java中String/StringBuffer/StringBuilder區別(轉)JavaUI
- Java 之String、StringBuffer 和 StringBuilder 三者區別介紹JavaUI
- Java中String,StringBuffer,StringBuilder基礎知識JavaUI
- 認識Java中String與StringBuffer以及StringBuilderJavaUI
- Java容器深入淺出之String、StringBuffer、StringBuilderJavaUI
- String, StringBuilder, StringBuffer 之間的區別 - JavaUIJava
- Java的JDK下StringBuffer與StringBuilder的區別JavaJDKUI
- JDK原始碼閱讀(3):AbstractStringBuilder、StringBuffer、StringBuilder類閱讀筆記JDK原始碼UI筆記
- 【資料結構】串(String、StringBuilder、StringBuffer)的JAVA程式碼實現資料結構UIJava
- String、StringBuffer、StringBuilder剖析UI