StringBuilder高性能用法總結(jié)

項(xiàng)目開發(fā)中很多時(shí)候我們都需要拼接字符串雇锡,那如何才能高效的完成字符串拼接呢拼岳?

指定初始容量

先來(lái)看一下StringBuilder的源碼(JDK7)

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

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity specified by the <code>capacity</code> argument.
     *
     * @param      capacity  the initial capacity.
     * @throws     NegativeArraySizeException  if the <code>capacity</code>
     *               argument is less than <code>0</code>.
     */
    public StringBuilder(int capacity) {
        super(capacity);
    }
}

StringBuilder的默認(rèn)構(gòu)造方法調(diào)用的是父類AbstractStringBuilder 中的AbstractStringBuilder(int capacity)構(gòu)造方法,如下:

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * 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];
    }
}



StringBuilder的內(nèi)部有一個(gè)char[], 在調(diào)用StringBuilder的無(wú)參構(gòu)造方法時(shí)其內(nèi)部char[]的默認(rèn)長(zhǎng)度是16。當(dāng)我們調(diào)用StringBuilder的append方法時(shí),其實(shí)就是不斷的往char[]里填東西的過程俯萌。

public StringBuilder append(String str) {
    super.append(str);
    return this;
}

其中,super.append是調(diào)用AbstractStringBuilder 的append(String str)方法上枕,如下:

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;
    return this;
}

StringBuilder的擴(kuò)容和ArrayList有些類似咐熙,具體代碼如下:

/**
 * This method has the same contract as ensureCapacity, but is
 * never synchronized.
 */
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
}

/**
 * This implements the expansion semantics of ensureCapacity with no
 * size check or synchronization.
 */
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);
}

StringBuilder默認(rèn)長(zhǎng)度是16,然后辨萍,如果要append第17個(gè)字符棋恼,怎么辦返弹?
答案是采用 Arrays.copyOf()成倍復(fù)制擴(kuò)容!
擴(kuò)容的性能代價(jià)是很嚴(yán)重的:一來(lái)有數(shù)組拷貝的成本爪飘,二來(lái)原來(lái)的char[]也白白浪費(fèi)了要被GC掉义起。可以想見师崎,一個(gè)129字符長(zhǎng)度的字符串默终,經(jīng)過了16,32犁罩,64, 128四次的復(fù)制和丟棄齐蔽,合共申請(qǐng)了496字符的數(shù)組,在高性能場(chǎng)景下床估,這幾乎不能忍含滴。

由此可見,合理設(shè)置一個(gè)初始值多重要丐巫。使用之前先仔細(xì)評(píng)估一下要保存的字符串最大長(zhǎng)度谈况。

復(fù)用StringBuilder

StringBuilder.setLength()方法只重置它的count指針,而char[]則會(huì)繼續(xù)重用递胧,源碼如下:

public void setLength(int newLength) {
    if (newLength < 0)
        throw new StringIndexOutOfBoundsException(newLength);
    ensureCapacityInternal(newLength);

    if (count < newLength) {
        for (; count < newLength; count++)
            value[count] = '\0';
    } else {
        count = newLength;
    }
}

toString()方法:

public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

而toString()時(shí)會(huì)把當(dāng)前的count指針也作為參數(shù)傳給String的構(gòu)造函數(shù)碑韵,所以不用擔(dān)心把超過新內(nèi)容大小的舊內(nèi)容也傳進(jìn)去了《衅ⅲ可見泼诱,StringBuilder是完全可以被重用的。

具體示例如下:

String[] history_steps = ruleDetailInfo.getHistory_steps().split(",");
for (String step : history_steps){
    sb.setLength(0);
    sb.append(ruleDetailInfo.getBusiness_id()).append("\t").append(step);
    results.add(sb.toString());
}

+ 與 StringBuilder的區(qū)別

String s = "hello" + user.getName();

這一行代碼經(jīng)過javac編譯后的效果赊锚,的確等價(jià)于使用StringBuilder,但沒有設(shè)定長(zhǎng)度屉栓。

String s = new StringBuilder().append(“hello”).append(user.getName());

但是舷蒲,如果像下面這樣:

 String s = “hello ”;
// 中間插入了其他一些代碼
s = s + user.getName();

每一條語(yǔ)句,都會(huì)生成一個(gè)新的StringBuilder友多,這里就有了兩個(gè)StringBuilder牲平,性能就完全不一樣了。

如果是在循環(huán)體里s+=i; 就更加多得沒譜域滥,例如:

String str = "";
for(int i=0; i<10000;i++){
    str += i;
}

StringBuffer 與 StringBuilder區(qū)別

StringBuffer的源碼如下(JDK7):

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

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;
    
    public StringBuffer() {
        super(16);
    }

    public StringBuffer(int capacity) {
        super(capacity);
    }

    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

    public synchronized int length() {
        return count;
    }

    public synchronized int capacity() {
        return value.length;
    }

    public synchronized void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > value.length) {
            expandCapacity(minimumCapacity);
        }
    }

    public synchronized void setLength(int newLength) {
        super.setLength(newLength);
    }

    public synchronized StringBuffer append(Object obj) {
        super.append(String.valueOf(obj));
        return this;
    }

    public synchronized StringBuffer append(String str) {
        super.append(str);
        return this;
    }
    ......
}

StringBuilder源碼:

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

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    public StringBuilder() {
        super(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);
    }

    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

    // Appends the specified string builder to this sequence.
    private StringBuilder append(StringBuilder sb) {
        if (sb == null)
            return append("null");
        int len = sb.length();
        int newcount = count + len;
        if (newcount > value.length)
            expandCapacity(newcount);
        sb.getChars(0, len, value, count);
        count = newcount;
        return this;
    }

    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }
    ......
}

StringBuffer與StringBuilder都是繼承于AbstractStringBuilder纵柿,唯一的區(qū)別就是StringBuffer的函數(shù)上都有synchronized關(guān)鍵字。

小結(jié)

StringBuilder是非線程安全的启绰,所以不能在多線程環(huán)境下共享使用昂儒。StringBuilder在使用的時(shí)候一定要指定其初始大小,另外委可,對(duì)性能要求比較高的場(chǎng)景下渊跋,可以考慮用一個(gè)ThreadLocal 緩存可重用的StringBuilder。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拾酝,隨后出現(xiàn)的幾起案子燕少,更是在濱河造成了極大的恐慌,老刑警劉巖蒿囤,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件客们,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡材诽,警方通過查閱死者的電腦和手機(jī)底挫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)岳守,“玉大人凄敢,你說(shuō)我怎么就攤上這事∈。” “怎么了涝缝?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)譬重。 經(jīng)常有香客問我拒逮,道長(zhǎng),這世上最難降的妖魔是什么臀规? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任滩援,我火速辦了婚禮,結(jié)果婚禮上塔嬉,老公的妹妹穿的比我還像新娘玩徊。我一直安慰自己,他們只是感情好谨究,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布恩袱。 她就那樣靜靜地躺著,像睡著了一般胶哲。 火紅的嫁衣襯著肌膚如雪畔塔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天鸯屿,我揣著相機(jī)與錄音澈吨,去河邊找鬼。 笑死寄摆,一個(gè)胖子當(dāng)著我的面吹牛谅辣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婶恼,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼屈藐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼榔组!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起联逻,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搓扯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后包归,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锨推,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年公壤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了换可。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厦幅,死狀恐怖沾鳄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情确憨,我是刑警寧澤译荞,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站休弃,受9級(jí)特大地震影響吞歼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜塔猾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一篙骡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丈甸,春花似錦糯俗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祈匙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間天揖,已是汗流浹背夺欲。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留今膊,地道東北人些阅。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像斑唬,于是被迫代替她去往敵國(guó)和親市埋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黎泣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 真的懂String么抒倚?真的懂String里面的==與equals的差別么?我想說(shuō)原來(lái)可能我懂坷澡,但是后來(lái)就沒有...
    yzzCool閱讀 643評(píng)論 0 3
  • java筆記第一天 == 和 equals ==比較的比較的是兩個(gè)變量的值是否相等托呕,對(duì)于引用型變量表示的是兩個(gè)變量...
    jmychou閱讀 1,501評(píng)論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法频敛,內(nèi)部類的語(yǔ)法项郊,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法斟赚,線程的語(yǔ)...
    子非魚_t_閱讀 31,639評(píng)論 18 399
  • 學(xué)號(hào)01白立平 2017年着降,“醫(yī)改”這個(gè)名詞,想必對(duì)每一位醫(yī)務(wù)人員都很具沖擊力拗军。雖然早在2009年任洞,我國(guó)就已經(jīng)啟動(dòng)...
    白立平閱讀 190評(píng)論 2 3