JDK1.7-StringBuilder源碼詳解

 

       StringBuilder是一個可變字符序列 (字符串)。和String相比,兩者都是字符串,都是字符的有序排列;不同的是,String是不可變的,而StringBuilder是可變的。

       StringBuilderStringBuffer幾乎一樣,只是前者沒有同步處理,更加高效。因此,這兩者的操作邏輯幾乎是一樣的,可以通過一個抽象的父類AbstractStringBuilder來實現共同的方法,而只需要在StringBuffer的方法中加上synchronized關鍵字即可。這點可以在源碼中明顯感覺到。

        所以這裏就不討論StringBuffer了,只看StringBuilder,下面將結合AbstractStringBuilder一起來討論StringBuilder的應用。

StringBuffer主要就是幾個修改字符串的方法:append(),insert(),delete()

 

有序字符串

StringBuilderString一樣都是字符串,所以用同樣的方式來存儲字符:char數組。也是使用valuecount屬性來表示:

char[]value;

int count;

 

StringBuilder默認新建的長度爲16,如果有初始化的String,就再加16,相當於預留16的可變長度。

public StringBuilder() {

        super(16);

}

public StringBuilder(String str) {

        super(str.length() +16);

        append(str);

}

這裏調用了AbstractStringBuilder的構造函數,相當於初始化了一個長度爲16char數組,然後調用append來把初始化的String賦值進去。

AbstractStringBuilder(int capacity) {

        value =newchar[capacity];

}


 

 

append

接下來,我們看看StringBuilder.append(),這裏添加完之後,返回自身this,這樣可以連續的調用,很方便,如sb.append("Hello").append("World")

public StringBuilder append(Stringstr) {

        super.append(str);

        returnthis;

}

這裏實質上調用了AbstractStringBuilder.append(),如下:

public AbstractStringBuilder append(String str) {

        if (str ==null) str ="null";

        int len = str.length();

        ensureCapacityInternal(count + len);

        str.getChars(0, len, value, count);

        count += len;

        returnthis;

}

public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin) {

    // ……此處省略String的範圍判斷

        System.arraycopy(value,offset + srcBegin, dst,dstBegin,srcEnd - srcBegin);

}

從上面可以看出來,append()是在char數組的末尾添加:首先校驗value的長度;然後再把Stringchar數組追加到value後面。

通過ensureCapacityInternal()校驗value的長度,溢出則調用expandCapacity()來擴展。   擴展邏輯:每次擴充原來產度的(len+1)*2,上限爲Integer.MAX_VALUE,下限爲追加後長度minimumCapacity

可以看出如果超出這個大小,就會重新分配內存,創建一個更大的數組,並將原先的數組複製過來,再丟棄舊的數組。這個操作是比較耗性能的,要儘量避免,所以在創建StringBufferd時候要指定好容量,多預留一些空間。   同理Collection

void expandCapacity(int minimumCapacity) {

        int newCapacity =value.length * 2 + 2;

        if (newCapacity - minimumCapacity < 0)

            newCapacity = minimumCapacity;

        if (newCapacity < 0) {

            if (minimumCapacity < 0)           // overflow

                thrownew OutOfMemoryError();

            newCapacity = Integer.MAX_VALUE;

        }

    value = Arrays.copyOf(value, newCapacity);  // 往後擴充數組

}

public staticchar[] copyOf(char[] original,int newLength) {

        char[] copy =new char[newLength];

        System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

        return copy;

}

上面討論完了append(String),這是最常用的情況。

如果只有一個字符的時候,不要用append(String),而要用append(char),邏輯更簡單高效,直接在後面追加一個字符即可,不用再依賴String的屬性。這也是FindBugs所提倡的用法。

public AbstractStringBuilder append(char c) {

        ensureCapacityInternal(count + 1);

        value[count++] = c;

        returnthis;

}


 

insert

append()是在尾部追加,而想要中間插入的話,可以通過insert(),從下面代碼能看出來,和append()的邏輯不同的,只是在數組copy的時候,把valueoffset處分開,留一個insertStr的長度的空位,把insertStr插入:【str.getChars(value,offset)

public StringBuilder insert(int offset, Stringstr) {

        super.insert(offset, str);

        returnthis;

}

public AbstractStringBuilder insert(int offset, Stringstr) {

        if ((offset < 0) || (offset > length()))

            thrownew StringIndexOutOfBoundsException(offset);

        if (str ==null)

            str = "null";

        int len = str.length();

        ensureCapacityInternal(count + len);

        System.arraycopy(value, offset,value, offset + len,count - offset);

        str.getChars(value, offset);    //往空擋插入

        count += len;

        returnthis;

}


 

delete

使用delete()可以自由地刪除中間的字串。如下所示,實際上就是把end後的字串覆蓋到start的位置上,然後把count相應縮小,後面的沒有覆蓋的部分就自然無效了。

public StringBuilder delete(int start,int end) {

        super.delete(start, end);

        returnthis;

}

public AbstractStringBuilder delete(int start,int end) {

        if (start < 0)

            thrownew StringIndexOutOfBoundsException(start);

        if (end >count)

            end = count;

        if (start > end)

            thrownew StringIndexOutOfBoundsException();

        int len = end - start;

        if (len > 0) {             // 把end後的字串覆蓋到start後

            System.arraycopy(value, start+len,value, start, count-end);

            count -= len;          // 相應縮小count

        }

        returnthis;

}


 

 

發佈了44 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章