用大白話的方式講明白Java的StringBuilder、StringBuffer的擴容機制


StringBufferStringBuilder,都是繼承了AbstractStringBuilder,它們的底層char數組value默認的初始化容量是16,擴容只需要修改底層的char數組,兩者的擴容最終都會調用到AbstractStringBuilder類相同的方法:

直入正題,擴容的步驟:

  1. 新的字符串的長度超過了底層原char數組value的大小,才需要進行擴容
  2. 先嘗試默認擴容,將新容量變成 (value.length << 1) + 2 ,也就是兩倍的原數組長度再加二
  3. 若默認擴充后的值還是小於至少容量的值,直接擴充到當前需要的至少容量大小;
  4. 經過前兩步驟確定的新數組大小,若大於Interger.MAX_VALUE,則報異常,若小於等於0,則新數組大小改為Interger.MAX_VALUE -8
  5. 確定了新數組的值后,通過Arrays.copy(value,newCapactity)進行復制。最終給value數組完成擴容。

這樣擴容的目的,宏觀上是盡可能地減少擴容次數,提高效率。

肯定有同學會問,默認擴容為什么是兩倍的原數組長度 + 2 ?

因為源碼並無說明這樣設計的原因,所以根據我找到的資料結合我的推測,可能的原因有這些:

  • 考慮到在創建Sf,Sb設置的初始長度不大時(例如1),+2 可以很大地提升擴容的效率,減少擴容的次數
  • 在舊版本的JDK擴容語句是 (value.length + 1) * 2 先加一再乘2,推測原意思是擴容的話至少增添一個空間再乘2,兼顧到擴容的次數和要減少擴容過大浪費的空間
  • newCapacity(int)的傳入參數有可能是0,那么在參數是0的情況下,0<<1運算結果也是0,如果沒+2,那么在創建數組的時候會創建出MAX_ARRAY_SIZE大小,所以作為設計的安全性考慮,選擇了+2。(本人認為除了反射調用newCapacity,其他情況應該不會出現newCapacity(int)可以傳入0為參數)
  • append(str)后需補充分隔符所預留的位置,為了減少擴容次數 (個人感覺這點不太靠譜)

下面是在JDK1.8中AbstractStringBuilder有關計算擴容的方法:

//AbstractStringBuilder.java 

   private void ensureCapacityInternal(int minimumCapacity) {
    //若所需長度大於已有長度,才繼續進行擴容
    if (minimumCapacity - value.length > 0) {
        //通過Arrays.copyOf(),將舊value數組內容先復制到newCapacity大小的數組,再賦值給新value
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    	}
	}
    private int newCapacity(int minCapacity) {
        // 默認擴容:newCapacity = 兩倍的原長度 + 2
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {//默認擴容后還是小於所需長度
            newCapacity = minCapacity;//直接補充至所需長度
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)//newCapacity>MAX_ARRAY_SIZE 或者≤0會調用
            : newCapacity;
    }

    MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { //大小超出Integer范圍爆異常
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE) //返回minCapacity與MAX_ARRAY_SIZE最大值
            ? minCapacity : MAX_ARRAY_SIZE;
    }   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM