Java StringBuffer & StringBuilder 源碼分析

簡介

總所周知,StringBuffer 是線程安全的,是 JDK 1.0 加入的;StringBuilder 是線程不安全的,是 JDK 1.5 加入的。

String & StringBuffer & StringBuilder 類的定義

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

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

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

①、三者都實現(xiàn)了 CharSequence 接口。也就是說,CharSequence 定義了字符串操作的接口,三者完成具體的實現(xiàn);
②、Serializable 是可以序列化的標(biāo)志;
③、后兩者繼承了 AbstractStringBuilder 類,而這個類封裝了 StringBuilder 和
StringBuffer 大部分操作的實現(xiàn)。

AbstractStringBuilder

AbstractStringBuilder 是 StringBuffer & StringBuilder 兩者的父類。
相同點:兩者的構(gòu)造函數(shù)都是調(diào)用了其父類的構(gòu)造函數(shù);
不同點:StringBuffer 大部分重寫了其父類的方法,并加同步鎖(synchronized),速度較慢;而 StringBuilder 則是直接調(diào)用其父類的方法,速度更快。

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    char[] value;
    int count;  // value 數(shù)組中實際上存放的字符數(shù)目
    AbstractStringBuilder() {}
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    public int length() {
        return count; //返回的是實際存放的字符數(shù)目
    }
    public int capacity() {
        return value.length;  //返回的是內(nèi)置字符數(shù)組的長度
    }

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

    private void ensureCapacityInternal(int minimumCapacity) {
        //  如果需要擴(kuò)展到的容量 > 當(dāng)前字符數(shù)組長度,那么就正常擴(kuò)容
        if (minimumCapacity - value.length > 0)
            expandCapacity(minimumCapacity);
    }
    //用于保證字符數(shù)組長度的方法,ArrayList 中也使用這種動態(tài)擴(kuò)容的思想
    void expandCapacity(int minimumCapacity) {
        // 初始化新的容量大小 = 當(dāng)前字符串長度的 2 倍加 2
        int newCapacity = value.length * 2 + 2;
        // 新容量大小 < 傳進(jìn)來的最小容量,就用最小的容量作為新數(shù)組的容量
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        // 如果新的容量和最小容量都小于0
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            // 把容量設(shè)為Integer.MAX_VALUE
            newCapacity = Integer.MAX_VALUE;
        }
        // 創(chuàng)建容量大小為 newCapacity 的新數(shù)組
        value = Arrays.copyOf(value, newCapacity);
    }
    // 去除 value 字符數(shù)組中所有為空的元素,使其 count = value.length
    public void trimToSize() {
        if (count < value.length) {
            value = Arrays.copyOf(value, count);
        }
    }
    ...
}

屬性

由 AbstractStringBuilder 類可知,StringBuffer & StringBuilder 父類中封裝的字符數(shù)組沒有 final 修飾,也就說明了 StringBuffer & StringBuilder 中的字符數(shù)組可以被不斷修改。而 String 則相反。

方法

由于 StringBuffer & StringBuilder 大部分的方法都是直接調(diào)用或者重寫父類的方法,所以下面我們著重分析其父類 AbstractStringBuilder 的方法

構(gòu)造方法

StringBuffer & StringBuilder 兩者的構(gòu)造方法是一樣的,都需要調(diào)用父類的構(gòu)造方法。存儲的字符數(shù)組,也是在父類中定義的。

    public StringBuilder() {
        super(16);   // 默認(rèn)的容量的大小是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);
    }

append()

    public AbstractStringBuilder append(String str) {
        // 如果傳入的參數(shù)為 null,則直接調(diào)用 appendNull() 方法在后面追加 'n'、'u'、'l'、'l' 四個字符。
        if (str == null)
            return appendNull();
        int len = str.length();
        // 首先,調(diào)用動態(tài)擴(kuò)容方法
        ensureCapacityInternal(count + len);
        // 將 str 從 0 到 len-1 位置上的字符復(fù)制到字符數(shù)組 value 中,并從 count 
處開始存放(將 str 追加到 value 末尾)
        str.getChars(0, len, value, count);
        count += len; 
        // 返回對象本身,使其 append() 可以連續(xù)調(diào)用
        return this;
    }

delete()

刪除 [start,end) 區(qū)域的字符(包括 start,不包括 end)

    public AbstractStringBuilder delete(int start, int end) {
        // 健壯性的檢查
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        // 需要刪除的長度
        int len = end - start;
        if (len > 0) {
            // 核心語句:將下標(biāo) [start,end] 區(qū)域的位置,用 [end,count] 區(qū)域的字符進(jìn)行覆蓋。
            System.arraycopy(value, start+len, value, start, count-end);
            // 并更新 count,這樣就只能輸出[0,count] 區(qū)域的字符。
            count -= len;
        }
        return this;
    }

delete 的底層操作,并沒有真正的刪除字符,而是把后面的字符進(jìn)行前移,從而覆蓋。其中 deleteCharAt 也是一樣的原理。

insert()

在 offset 位置插入字符串 str

    public AbstractStringBuilder insert(int offset, String str) {
        if ((offset < 0) || (offset > length()))
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null)
            str = "null";
        int len = str.length();
        // 調(diào)用擴(kuò)容方法
        ensureCapacityInternal(count + len);
        // 將 [offset,count] 區(qū)域字符串向后移動 len 個位置,為插入字符串留出空間
        System.arraycopy(value, offset, value, offset + len, count - offset);
        // 將 str 復(fù)制到 value 字符數(shù)組中 offset 之后的位置
        str.getChars(value, offset);
        // 更新當(dāng)前對象中記錄的長度
        count += len;
        return this;
    }

該 insert 方法有很多重載,但是本質(zhì)上都離不開我們上述介紹的這個方法。

replace()

將 [start,end) 區(qū)域的字符串替換成 str

    public AbstractStringBuilder replace(int start, int end, String str) {
        // 健壯性的檢查
        if (start < 0)
            throw new StringIndexOutOfBoundsException(start);
        if (start > count)
            throw new StringIndexOutOfBoundsException("start > length()");
        if (start > end)
            throw new StringIndexOutOfBoundsException("start > end");
        if (end > count)
            end = count;
        // 獲取需要添加的字符串的長度
        int len = str.length();
        // 計算新字符串的長度
        int newCount = count + len - (end - start);
        // 調(diào)用擴(kuò)容方法
        ensureCapacityInternal(newCount);
        // 將從 end 開始到最后的字符,向后移動到 start+len 的位置
        System.arraycopy(value, end, value, start + len, count - end);
        // 將 str 復(fù)制到 value 字符數(shù)組中 start 后面
        str.getChars(value, start);
        // 更新字符串長度
        count = newCount;
        return this;
    }

toString()

返回了一個新的 String 對象,與原來的對象不共享內(nèi)存

    public String toString() {
        return new String(value, 0, count);
    }

擴(kuò)展

①、在編譯階段就能夠確定的字符串常量,完全沒有必要創(chuàng)建String或StringBuffer對象。直接使用字符串常量的"+"連接操作效率最高;
②、StringBuffer & StringBuilder 對象的 append 效率要高于String 對象的"+"連接操作;
③、在不考慮線程安全的情況下,首選 StringBuilder 類,效率最高。否則選擇 StringBuffer。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,673評論 18 399
  • Tip:筆者馬上畢業(yè)了,準(zhǔn)備開始 Java 的進(jìn)階學(xué)習(xí)計劃。于是打算先從 String 類的源碼分析入手,作為后面...
    石先閱讀 12,107評論 16 58
  • 1.import static是Java 5增加的功能,就是將Import類中的靜態(tài)方法,可以作為本類的靜態(tài)方法來...
    XLsn0w閱讀 1,425評論 0 2
  • 突然覺得,一生只在一個城市生活,長大,上學(xué),工作,變老,死去,是一件很可怕的事。 對啊,是可以去旅游,但是旅游和在...
    一丟丟DeiDeiBei閱讀 165評論 1 0
  • Life isn’t always beautiful, but the struggles make you s...
    靜靜很快樂987閱讀 152評論 0 0

友情鏈接更多精彩內(nèi)容