Java StringBuilder和StringBuffer源碼分析

jopen 8年前發布 | 13K 次閱讀 Java 源碼分析 Java開發

簡介

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

來自: http://segmentfault.com/a/1190000004261063

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!