StringBuilder 與 StringBuffer 源碼分析

簡介

    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 大部分操做的實現。安全

AbstractStringBuilder

變量及構造方法

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

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() 能夠連續調用。this

StringBuilder

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 對象賦初始值。線程

append()方法

public StringBuilder append(String str) {
    super.append(str);
    return this;
}
public StringBuilder append(CharSequence s) {
    super.append(s);
    return this;
}

append() 的重載方法不少,這裏隨便列舉了兩個。顯然,這裏是直接調用的父類 AbstractStringBuilder 中的方法。code

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,最好預先估計好字符串的大小避免擴容帶來的時間消耗。

相關文章
相關標籤/搜索