Java基礎--String、StringBuffer和StringBuilder

之前我們在另一篇博客講過,String是被final修飾的,是一個不可變對象。(點擊這裏查看)我們如果要修改String的內容,就只能通過重新new一個對象來實現。毫無疑問,這是非常耗內存的,當我們需要不斷的更改String對象的內容時,我們的內存空間很容易溢出。因此,我們有了StringBuffer和StringBuilder這兩個替代品。

StringBuffer

StringBuffer類繼承自AbstractStringBuilder抽象類,實現了Serializable序列化接口和CharSequence接口。

StringBuffer的構造方法

構造方法
Constructor and Description
StringBuffer()

構造一個沒有字符的字符串緩衝區,初始容量爲16個字符。

StringBuffer(CharSequence seq)

構造一個包含與指定的相同字符的字符串緩衝區 CharSequence 。

StringBuffer(int capacity)

構造一個沒有字符的字符串緩衝區和指定的初始容量。

StringBuffer(String str)

構造一個初始化爲指定字符串內容的字符串緩衝區。

StringBuffer的常用方法

StringBuffer append(String str)

將指定的字符串附加到此字符序列。

char charAt(int index)

返回 char在指定索引在這個序列值。

StringBuffer delete(int start, int end)

刪除此序列的子字符串中的字符。

int indexOf(String str)

返回指定子字符串第一次出現的字符串內的索引。

StringBuffer insert(int offset, String str)

將字符串插入到此字符序列中。

int length()

返回長度(字符數)。

String substring(int start)

返回一個新的 String ,其中包含此字符序列中當前包含的字符的子序列。

String substring(int start, int end)

返回一個新的 String ,其中包含此序列中當前包含的字符的子序列

StringBuffer初始化及擴容機制

StringBuffer與String最大的不同在於StringBuffer是可變的對象,它可以根據要存儲的字符串長度來改變自己的容量,這離不開它本身的擴容機制。首先我們通過StringBuffer的構造函數來了解它不同的初始化方式。

  • StringBuffer():StringBuffer的初始容量可以容納16個字符,當該對象的實體存放的字符長度大於16時,實體容量就自動增加。StringBuffer對象可以通過length()方法獲取實體中存放的字符序列長度,通過capacity()方法來獲取當前實體的實際容量。
  • StringBuffer(int size):可以指定分配給該對象的實體的初始容量參數爲參數size指定的字符個數。當該對象的實體存放的字符序列的長度大於size個字符時,實體的容量就自動的增加。以便存放所增加的字符。
  • StringBuffer(String s):可以指定給對象的實體的初始容量爲參數字符串s的長度額外再加16個字符。當該對象的實體存放的字符序列長度大於size個字符時,實體的容量自動的增加,以便存放所增加的字符。

接下來我們來通過append()方法來了解它的擴容方法。

使用append()方法在字符後面追加東西的時候,如果長度超過了該字符串存儲空間大小了就需要進行擴容:構建新的存儲空間,將舊的複製過去。我們可以從它的源碼很容易看出來這個過程:

//AbstractStringBuilder的方法
public AbstractStringBuilder append(String str) {
        //判空
        if (str == null) str = "null";
        int len = str.length();

        //判斷是否足夠存儲str字符串,若不夠則調用expandCapacity方法進行擴容
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
}



//判斷是否需要擴容
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);
    }



//將字符串複製過去 String類的方法
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }


        /*  srcBegin - 以此偏移開始複製。
            srcEnd - 在此偏移處停止複製。
            dst - 將數據複製到的數組。
            dstBegin - 偏移到 dst 。*/


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

從源碼我們可以更加清晰的瞭解StringBuffer的擴容過程。當我們進行字符串append時,會先計算添加後字符串大小,傳入一個方法ensureCapacityInternal()進行擴容判斷,需要擴容則調用expandCapacity方法進行擴容。嘗試將新容量擴爲原大小的2倍+2,然後再進行一次if判斷,容量如果不夠,直接擴充到需要的容量大小,即更新後字符串的大小。

StringBuilder和StringBuffer

StringBuilder所繼承的抽象類和實現的接口都與StringBuffer一模一樣,他們兩者的方法和功能完全是等價的。只是StringBuffer中的方法大都採用synchronized關鍵字進行修飾,因此是線程安全的,而StringBuilder沒有,可以被認爲是線程不安全的。由於使用了synchronized關鍵字,所以在單線程程序下,StringBuilder效率更快。

總結

  1. 從字符串底層存儲對象來看,String是不可變對象,而StringBuilder和StringBuffer是可變長度對象。
  2. 從線程安全來看,StringBuffer是線程安全對象,因爲很多方法被synchronized方法修飾,而String和StringBuilder是線程不安全的。
  3. 從效率來看,大部分情況下StringBuilder>StringBuffer>String.

參考:https://blog.csdn.net/longfulong/article/details/78700239

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章