StringBuffer 和 StringBuilder 總結

StringBuffer 和 StringBuilder

介紹

大多數狀況下, StringBuffer 的速度要比 String 快; StringBuilder 要比StringBuffer快;StringBuffer 和 StringBuilder 都是 AbstractStringBuilder 的子類,區別在於StringBuffer 的方法大部分都有 synchronized 修飾。java

源碼解析

AbstractStringBuilder

變量及構造方法

/**
 * 用來存儲字符的數組
 * The value is used for character storage.
 */
char[] value;

/**
 * 字符個數 
 * The count is the number of characters used.
 */
int count;

/**
 * This no-arg constructor is necessary for serialization of subclasses.
 */
AbstractStringBuilder() {
}

/**
 * 在構造方法中指定字符數組的長度
 * Creates an AbstractStringBuilder of the specified capacity.
 */
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

擴容

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

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

private int hugeCapacity(int minCapacity) {
    if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
        throw new OutOfMemoryError();
    }
    return (minCapacity > MAX_ARRAY_SIZE)
        ? minCapacity : MAX_ARRAY_SIZE;
}

擴容的方法最終由 newCapacity() 實現的,首先將容量左移一位(即擴大2倍)同時加2,若是此時任小於指定的容量,那麼就將容量設置爲 minimumCapacity 。而後判斷是否溢出,經過 hugeCapacity 實現,若是溢出了(長度大於 Integer.MAX_VALUE ),則拋錯( OutOfMemoryError );不然根據 minCapacityInteger.MAX_VALUE - 8 的大小比較肯定數組容量爲 max(minCapacity, Integer.MAX_VALUE - 8)。最後將 value 值進行拷貝,這一步顯然是最耗時的操做。c++

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() 是最經常使用的方法,它有不少形式的重載。上面是最經常使用的一種,用於追加字符串。若是 strnull ,則直接調用
appendNull() 方法。這個方法就是直接追加 'n''u''l''l' 這幾個字符,方法以下:git

private AbstractStringBuilder appendNull() {
    int c = count;
    ensureCapacityInternal(c + 4);
    final char[] value = this.value;
    value[c++] = 'n';
    value[c++] = 'u';
    value[c++] = 'l';
    value[c++] = 'l';
    count = c;
    return this;
}

若是不是null,則首先須要判斷數組容量是否足夠,不夠則須要擴容(擴容則是調用上述分析的擴容方法);
而後調用 StringgetChars() 方法將 str 追加到 value 末尾;
最後返回對象自己,因此 append() 能夠連續調用(就是一種相似於鏈式編程)。github

思考

  • 爲何每次擴容是擴容爲原來的兩倍?
    我的以爲是爲了不常常擴容帶來的成本消耗。
  • 爲何會加2呢?
    我的也沒想出什麼好的解釋,以爲多是由於 Java 開發者認爲咱們在 append 數據的時候,中間常常會加一個分隔符,剛好這個分隔符在 Java 中正好佔用兩個字節。也不知道分析的對不對,有其餘意見的大佬們能夠在 issue
    中進行討論。

StringBuilder 與 StringBuffer 方法對比

經過查看源碼分析發現二者都繼承至 AbstractStringBuilder 。 而 StringBuffer 之因此是線程安全的,是由於重寫 AbstractStringBuilder 的方法的時候在前面加上了 synchronzied 修飾這些方法;而 StringBuilder 重寫的時候只是直接調用父類的方法,沒有作其餘的操做。編程

其實經過閱讀源碼發現 StringBuilderStringBuffer 之間的關係,相似於 HashMapHashTable 之間的關係。數組

相關文章
相關標籤/搜索