Java下String和StringBuilder的append性能解析

源代碼是萬物之源率寡。——黑客帝國

先看下String的源代碼:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];//char數(shù)組也就是String的值

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;
}

所以其實String就是一個char[]疏虫。唯一需要注意的點應(yīng)該就是final修飾符坯临,也就意味著value是常量不可更改老速。這個和之前object-c中的設(shè)計很像,但不知道在這里是不是為了線程安全磺樱。從后面replace等函數(shù)的實現(xiàn)也可以看出,更改String的值需要重新申請一個String變量。

    public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }

/* avoid getfield opcode */
關(guān)于這個注釋,查了一下。得到的答案是將變量復(fù)制到局部變量中矩父,這樣在下面的循環(huán)操作中就可以不用反復(fù)的從堆中取數(shù)據(jù)了。(棧中的訪問速度更快蝗茁,那是不是操作次數(shù)比較多的變量都可以用這種方法去增加速度呢署驻?)

但是在這個文件夾中我沒有找到+號的重載函數(shù)(當(dāng)然java本身不允許重載運算符),經(jīng)過百度發(fā)現(xiàn)這個加號的重載是在編譯階段實現(xiàn)的。

//以下兩者是等價的
s = i + ""
s = String.valueOf(i);
//以下兩者也是等價的
= "abc" + i;
= new StringBuilder("abc").append(i).toString();

再看下StringBuilder的源代碼:

  /**
     * 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() {
    }

可變的char[]乖订,以及長度。

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

這里對于為什么要在初始化的時候預(yù)留一個16個大小的數(shù)組還不太明白。

   public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

   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);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

這里就不貼所有的代碼了攀芯,主要就是如果是對象則會調(diào)用string的valueof()轉(zhuǎn)成String,如果是String就是調(diào)用getchars()文虏,再對char[]添加賦值侣诺。
所以其性能差異即在少做了一步string和StringBuilder的轉(zhuǎn)化。
而String s = "abc";這樣的操作會在常量字符區(qū)生成一個"abc"常量氧秘,也會增加開銷年鸳。

java,StringBuilder預(yù)留16位

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);//在添加String時確定內(nèi)部空間足夠
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0)//如果超過了預(yù)留的空間大小丸相,則選擇擴容搔确。
            expandCapacity(minimumCapacity);
    }
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;//將其擴充為2倍的大小加2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;//如果還不夠大小灭忠,則將空間擴充為兩個字符串大小之和
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;//如果字符串之和小于int最大值膳算,但是兩倍太大會導(dǎo)致overflow,則將其設(shè)置為int最大值
        }
        value = Arrays.copyOf(value, newCapacity);
    }

這樣做最大的好處應(yīng)該是在擴展小的字符串時不用每次都申請空間弛作,只有在原有空間已滿的時候再進行擴容涕蜂,典型的空間換時間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末映琳,一起剝皮案震驚了整個濱河市机隙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萨西,老刑警劉巖黍瞧,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異原杂,居然都是意外死亡印颤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門穿肄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來年局,“玉大人,你說我怎么就攤上這事咸产∈阜瘢” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵脑溢,是天一觀的道長僵朗。 經(jīng)常有香客問我赖欣,道長,這世上最難降的妖魔是什么验庙? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任顶吮,我火速辦了婚禮,結(jié)果婚禮上粪薛,老公的妹妹穿的比我還像新娘悴了。我一直安慰自己,他們只是感情好违寿,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布湃交。 她就那樣靜靜地躺著,像睡著了一般藤巢。 火紅的嫁衣襯著肌膚如雪搞莺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天掂咒,我揣著相機與錄音腮敌,去河邊找鬼。 笑死俏扩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弊添。 我是一名探鬼主播录淡,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼油坝!你這毒婦竟也來了嫉戚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤澈圈,失蹤者是張志新(化名)和其女友劉穎彬檀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞬女,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡窍帝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了诽偷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坤学。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖报慕,靈堂內(nèi)的尸體忽然破棺而出深浮,到底是詐尸還是另有隱情,我是刑警寧澤眠冈,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布飞苇,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏布卡。R本人自食惡果不足惜雨让,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望羽利。 院中可真熱鬧宫患,春花似錦、人聲如沸这弧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匾浪。三九已至皇帮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛋辈,已是汗流浹背属拾。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冷溶,地道東北人渐白。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像逞频,于是被迫代替她去往敵國和親纯衍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

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

  • Tip:筆者馬上畢業(yè)了苗胀,準(zhǔn)備開始 Java 的進階學(xué)習(xí)計劃襟诸。于是打算先從 String 類的源碼分析入手,作為后面...
    石先閱讀 12,018評論 16 58
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法基协,類相關(guān)的語法歌亲,內(nèi)部類的語法,繼承相關(guān)的語法澜驮,異常的語法陷揪,線程的語...
    子非魚_t_閱讀 31,662評論 18 399
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,504評論 0 3
  • 下面的內(nèi)容是對網(wǎng)上原有的Java面試題集及答案進行了全面修訂之后給出的負(fù)責(zé)任的題目和答案杂穷,原來的題目中有很多重復(fù)題...
    獨念白閱讀 1,349評論 0 3
  • 有/沒有班車坐,千萬個不一樣拴鸵! 1最近坐班車上癮了$枳梗現(xiàn)在開車優(yōu)勢全無蜗搔,一路擁堵+限行,車技八堡、保險理賠知識雖說被迫增...
    愛拆書的東哥閱讀 847評論 6 2