聊聊 String、StringBuffer、StringBuilder

String

String是Java語(yǔ)言非常重要的類,提供了構(gòu)造和管理字符串的各種基本邏輯, 是 Immutable 類的典型實(shí)現(xiàn),原生的保證了基礎(chǔ)線程安全,因?yàn)槟銦o(wú)法對(duì)它內(nèi)部數(shù)據(jù)進(jìn)行任何修改,這種便利甚至體現(xiàn)在拷貝構(gòu)造函數(shù)中,由于不可變,Immutable 對(duì)象在拷貝時(shí)不需要額外復(fù)制數(shù)據(jù)。String 類被聲明成為 final class,所有屬性也都是 final 的。也由于它的不可變性,類似拼接、裁剪字符串等動(dòng)作,都會(huì)產(chǎn)生新的 String 對(duì)象,相關(guān)操作的效率往往對(duì)應(yīng)用性能有明顯影響,所以在拼接、裁剪字符串操作一般使用 StringBuffer 、StringBuilder 類。在數(shù)據(jù)存儲(chǔ)方面,從 Java 9 開始引入了 Compact String 設(shè)計(jì),將存儲(chǔ)方式從原來(lái)的 char 數(shù)組改變成 byte 數(shù)組和一個(gè)標(biāo)識(shí)編碼 coder ,這樣實(shí)現(xiàn)了節(jié)約空間的目的,因?yàn)?char 是兩個(gè) bytes 的大小,而一些拉丁系語(yǔ)言的字符不需要太寬的 char ,這樣會(huì)對(duì)空間造成浪費(fèi)。來(lái)看看 String 的部分源碼。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    @Stable
    private final byte[] value;
    private final byte coder;
    static final boolean COMPACT_STRINGS;

    static {
        COMPACT_STRINGS = true;
    }
    public String() {
        this.value = "".value;
        this.coder = "".coder;
    }
    public String(int[] codePoints, int offset, int count) {
        checkBoundsOffCount(offset, count, codePoints.length);
        if (count == 0) {
            this.value = "".value;
            this.coder = "".coder;
            return;
        }
        // 加入 compact_string 判斷
        if (COMPACT_STRINGS) {
            byte[] val = StringLatin1.toBytes(codePoints, offset, count);
            if (val != null) {
                this.coder = LATIN1;
                this.value = val;
                return;
            }
        }
        this.coder = UTF16;
        this.value = StringUTF16.toBytes(codePoints, offset, count);
    }
    ......
}

你可以對(duì)比一下 Java 9 與 Java 9 之前的 String 的實(shí)現(xiàn)。

  • 不可變對(duì)象與可變對(duì)象

在面向?qū)ο蠛秃瘮?shù)式編程中,一個(gè) immutable 對(duì)象(不可變對(duì)象)是指一旦創(chuàng)建之后狀態(tài)不可改變的對(duì)象。mutable 對(duì)象(可變對(duì)象)是指創(chuàng)建之后也可以修改的對(duì)象。在有些情況下,對(duì)象也被認(rèn)為是不可變的(immutable),即,一個(gè)對(duì)象包含的內(nèi)部使用的屬性改變了,但從外部看對(duì)象的狀態(tài)并沒(méi)有改變。例如,一個(gè)使用 memoization 來(lái)緩存復(fù)雜計(jì)算結(jié)果的對(duì)象仍然被看作是不可變(immutable)對(duì)象。
不可變對(duì)象(immutable)有幾個(gè)優(yōu)點(diǎn):

  • 提高可讀性和運(yùn)行效率
  • 線程安全
  • 比可變對(duì)象有更高的安全性

StringBuffer

StringBuffer 是為解決 String 對(duì)象拼接產(chǎn)生太多中間對(duì)象的問(wèn)題而提供的一個(gè)類,可以理解為是 String 的補(bǔ)充。StringBuffer 提供了 append 和 add 方法,用于把字符串添加到已有序列的末尾或者指定位置。StringBuffer 繼承了 AbstractStringBuilder ,StringBuffer 是一個(gè)線程安全的可修改字符序列,它保證了線程安全,也隨之帶來(lái)了額外的性能開銷。它在所有的操作方法前面都添加了 synchronized 關(guān)鍵字來(lái)實(shí)現(xiàn)線程安全。簡(jiǎn)單粗暴。由于它的目的是修改字符串序列,所以在 StringBuffer 的內(nèi)部提供了一個(gè)數(shù)組,那么這個(gè)數(shù)組是多大呢?目前的實(shí)現(xiàn)中,初始構(gòu)建的字符串長(zhǎng)度+ 16 。當(dāng)操作的字符串長(zhǎng)度大于數(shù)組長(zhǎng)度時(shí),它會(huì)自動(dòng)擴(kuò)容,重新創(chuàng)建新的數(shù)組,拋棄原來(lái)的數(shù)組,利用 arraycopy 將原來(lái)的數(shù)組內(nèi)容復(fù)制到新的數(shù)組中,每次擴(kuò)容的大小是原來(lái)數(shù)組的兩倍+ 2 。頻繁的擴(kuò)容會(huì)損耗性能,所以在使用時(shí)我們要估摸拼接字符串的長(zhǎng)度,設(shè)置合理的大小。來(lái)一起看看 StringBuffer 的部分源碼。

 private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = value.length >> coder;
        // 新數(shù)組是老數(shù)組大小的兩倍+ 2
        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;
    }


 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuffer>, CharSequence
{
    // 初始化數(shù)組的大小
    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }
    ......
    // 使用 synchronized 來(lái)實(shí)現(xiàn)線程安全
    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }
    ......
}

StringBuilder

StringBuilder 是 Java 1.5 中新增的,在能力上和 StringBuffer 沒(méi)有本質(zhì)區(qū)別,但是它去掉了線程安全的部分,有效減小了開銷,是絕大部分情況下進(jìn)行字符串拼接的首選。

    // 和 StringBuffer 的實(shí)現(xiàn)一模一樣,只是沒(méi)有保證線程安全,這樣提升了使用性能
    @Override
    @HotSpotIntrinsicCandidate
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

對(duì)比 StringBuffer 與 StringBuilder 的源碼,StringBuilder 只是在操作方法上把 synchronized 關(guān)鍵字去掉了,沒(méi)有其他變化。

總結(jié)

  • String 是不可變類型,不適合字符串的拼接、裁剪
  • StringBuffer 是對(duì) String 的補(bǔ)充,能在保證線程安全的情況下拼接、裁剪字符串
  • StringBuilder 是對(duì) StringBuffer 的優(yōu)化,不能保證線程安全,性能比 StringBuffer 較好,是絕大多數(shù)字符串拼接、裁剪的首選類
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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