JDK原始碼閱讀(3):AbstractStringBuilder、StringBuffer、StringBuilder類閱讀筆記

pedro7 發表於 2021-10-09

AbstractStringBuilder

abstract class AbstractStringBuilder implements Appendable, CharSequence{
    ...
}

1. inflate方法

inflate()方法可以將當前的Latin1編碼的字串膨脹到16位,即UTF16編碼(Latin1編碼為8位)

/**
 * If the coder is "isLatin1", this inflates the internal 8-bit storage
 * to 16-bit <hi=0, low> pair storage.
*/
private void inflate() {
    if (!isLatin1()) {
        return;
    }
    byte[] buf = StringUTF16.newBytesFor(value.length);
    StringLatin1.inflate(value, 0, buf, 0, count);
    this.value = buf;
    this.coder = UTF16;
}

2. ensureCapacity方法

public void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > 0) {
        ensureCapacityInternal(minimumCapacity);
    }
}

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    int oldCapacity = value.length >> coder;
    if (minimumCapacity - oldCapacity > 0) {
        // 拷貝過去
        value = Arrays.copyOf(value, newCapacity(minimumCapacity) << coder);
    }
}

private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = value.length >> coder;
    int newCapacity = (oldCapacity << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    int SAFE_BOUND = MAX_ARRAY_SIZE >> coder;
    return (newCapacity <= 0 || SAFE_BOUND - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

ensureCapacity(miniCapacity)方法保證當前容器的大小不小於miniCapacity,如果小於的話就要進行擴容處理。擴容成下面兩者中的較大值。

  • miniCapacity
  • 2*oldCapacity

3. setCharAt方法

setCharAt()方法可以將index處的字元設為ch

public void setCharAt(int index, char ch) {
    checkIndex(index, count);
    if (isLatin1() && StringLatin1.canEncode(ch)) {
        value[index] = (byte)ch;
    } else {
        if (isLatin1()) {
            inflate();
        }
        StringUTF16.putCharSB(value, index, ch);
    }
}

4. append方法

append()方法根據引數型別不同,在字串後面追加。

public AbstractStringBuilder append(CharSequence s) {
    if (s == null) {
        return appendNull();
    }
    if (s instanceof String) {
        return this.append((String)s);
    }
    if (s instanceof AbstractStringBuilder) {
        return this.append((AbstractStringBuilder)s);
    }
    return this.append(s, 0, s.length());
}

值得一提的是這個appendNull()方法,當我們在試圖append一個空物件時,它居然選擇在builder串後面加上一個“null”,看下面的示例。

public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder("a");
    String s = null;
    stringBuilder.append(s);
    System.out.println(stringBuilder.toString());
    System.out.println(stringBuilder.toString().length());
}

輸出結果居然是

anull
5

也就是說,當append空物件時,會append一個“null”字串。

另外,當我們嘗試append一個boolean物件時,會append一個“true”串或者“false”串。

5. delete方法

delete方法刪除builder中的一段

public AbstractStringBuilder delete(int start, int end) {
    int count = this.count;
    if (end > count) {
        end = count;
    }
    checkRangeSIOOBE(start, end, count);
    int len = end - start;
    if (len > 0) {
        shift(end, -len);
        this.count = count - len;
    }
    return this;
}

類似地還有deleteCharAt

public AbstractStringBuilder deleteCharAt(int index) {
    checkIndex(index, count);
    shift(index + 1, -1);
    count--;
    return this;
}

6. reverse方法

reverse()方法可以逆轉字元的順序。

public AbstractStringBuilder reverse() {
    byte[] val = this.value;
    int count = this.count;
    int coder = this.coder;
    int n = count - 1;
    if (COMPACT_STRINGS && coder == LATIN1) {
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            byte cj = val[j];
            val[j] = val[k];
            val[k] = cj;
        }
    } else {
        StringUTF16.reverse(val, count);
    }
    return this;
}

實際的方法就是雙指標靠攏+交換、

StringBuffer

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuffer>, CharSequence
{

StringBuffer類幾乎所有方法都是通過super關鍵字呼叫AbstractStringBuilder父類的方法。

StringBuffer實現同步的方式就是給所有方法加上synchronized關鍵字。

1. toStringCache變數

/**
   * A cache of the last value returned by toString. Cleared
   * whenever the StringBuffer is modified.
*/
private transient String toStringCache;

toStringCache快取了toString()方法的最後一次呼叫的值,在每次StringBuffer被修改前這個快取會清除。

toString()方法中檢查如果有快取,就直接返回快取。這樣不用每次都處理toString()的過程,而只在需要的時候處理。

public synchronized String toString() {
    if (toStringCache == null) {
        return toStringCache =
            isLatin1() ? StringLatin1.newString(value, 0, count)
            : StringUTF16.newString(value, 0, count);
    }
    return new String(toStringCache);
}

StringBuilder

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
{
    ...
}

StringBuilder類幾乎所有方法都是通過super關鍵字呼叫AbstractStringBuilder父類的方法。