前文
最近也快到了金三銀四, 想該篇文章這種問題,貌似又有了熱度 :
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM0QzNzEzYjJmNyIjMkhjNzYzXyQDOzcTM3AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
這種問題存在疑惑嗎? 如果你存在? 看完這篇你就沒疑惑了 。
這一篇結合源碼還有小代碼例子, 還有我的唠叨,我們還是一貫作風,學知識,跟着我,隻學一遍,忘不掉!
正文
不多說,開整:
JDK 1.8
第一行代碼,new一個ArrayList出來 :
List<Integer> testList = new ArrayList<>();
然後點進去看源碼, 跟着我思路來,我們一起玩一玩這個ArrayList :
草圖:
如果耐心看完這個圖,大家應該其實心裡面對于前文提到的問題已經有一些結果了,
①arrayList 底層是個 數組, Object[] elementData ;
②size是 這個arrayList 的 底層數組 Object[] elementData 包含的元素 ,記住了是包含,而不是 這個數組的 length (length是注意點了,數組的length其實說白了就是所謂的容量);
③其餘就是2個空的數組,具體在哪裡被調用被使用,源碼裡面随便點一下就可以看到;
④ DEFAULT_CAPACITY 這個變量的注釋,有點小怪,預設初始容量 ,但是記住,我們以1.8源碼為準,眼見為實 。因為可以看到 :
第一句話: ArrayList的容量是該數組緩沖區的長度(上文已經說到了)。
第二句話,如果一個空的ArrayList 被第一次add的時候, DEFAULT_CAPACITY=10這個值會被用上。
是以到了這裡, 再看一下new的時候調用的初始化構造函數,我們基本上就 心無任何疑惑了:
一個空的數組,那它的length就是 這個arrayList的 容量, 是多少? 顯然是 0 .
證據,我們直接反射拿出來這個elementData數組,就是要看它的length:
public class DoTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
List<Integer> testList = new ArrayList<>();
Class<ArrayList> arrayListClass = ArrayList.class;
Field field = arrayListClass.getDeclaredField("elementData");
field.setAccessible(true);
Object[] object1 = (Object[]) field.get(testList);
//傳回目前ArrayList執行個體的容量值
System.out.println("這時候容量是多少:" + object1.length);
}
}
運作結果:
是以結論一 核實:
jdk 1.8 , new 一個 arraylist ,初始化的容量是 0 .
那麼繼續,核實一下 什麼時候 容量 變成10 ?
根據源碼的注釋,寫着,如果是通過無參構造函數new 出來的arraylist (有參都直接指定容量了不多說了), 第一個元素 add進去的時候,容量會 賦予為 DEFAULT_CAPACITY = 10;
直接看下我們的例子代碼先 :
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
List<Integer> testList = new ArrayList<>();
Class<ArrayList> arrayListClass = ArrayList.class;
Field field = arrayListClass.getDeclaredField("elementData");
field.setAccessible(true);
Object[] object1 = (Object[]) field.get(testList);
//傳回目前ArrayList執行個體的容量值
System.out.println("這時候容量是多少:" + object1.length);
testList.add(100);
Object[] object2 = (Object[]) field.get(testList);
System.out.println("第一個值add完了之後,這時候容量是多少:" + object2.length);
}
}
運作結果:
此時雖然可以下結論,但是我們再結合源碼看看,到底怎麼變成10的 :
第一小段代碼:
按照我們第一次add, size肯定是0了, 0+1 =1 ,是以 ensureCapacityInternal 這個函數傳入的是 1 ;
第二段小代碼:
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,顯然我們new出來的,就是等于的:
這時候觸發的是 Match.max比較, 10和 1比較最大值,那當然是10 了 。
是以 ensureExplicitCapacity 函數被調用,傳入的 參數值是10 ;
第四段小代碼:
可以看到, 擴容函數被觸發了, grow(10) ,看到這裡應該知道這個10容量其實就是第一次add的時候,擴容函數觸發賦予的容量值 10 ;
最後, 順便看看擴容函數 grow :
代碼非常簡單:
核心的幾個小代碼,我們一起看看 :
int newCapacity = oldCapacity + (oldCapacity >> 1);
新的容量 等于 舊的 容量 + 舊的容量的一半, 那麼也就是 變成舊的容量的 1.5倍 :
然後就是兩個if判斷了 ,
就拿我們的第一個add觸發擴容來說, 這時候傳入的 minCapacity是 10 ,而newCapacity=0+0的一半還是0,是以觸發的是 newCapacity =10 ;
elementData = Arrays.copyOf(elementData, newCapacity); 容量就變成10了~
if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
另外一個if ,
if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
也就是當計算出來的newCapacity ,比最多允許的容量值還大,怎麼處理? 答案是,最大就給最大值。
沒完, 最大值是多少? 源碼也有說 :
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
為什麼需要 -8 ?
注釋上寫的明明白白(預留了一些空間 存自己的東西):