我們在使用ArrayList的時候,了解到默認(rèn)初始化容量是10慎菲,在Java 1.8的情況下,我們看下真實(shí)情況如何锨并。我們先看一個Demo代碼露该,執(zhí)行步驟如下:
- 初始化一個列表,查看容量
- 向列表添加一個元素第煮,查看容量
- 向列表添加十個元素解幼,查看容量
List<String> arrayList = new ArrayList<>();
Class<? extends List> arrayClass = arrayList.getClass();
Field field = arrayClass.getDeclaredField("elementData");
field.setAccessible(true);
Object[] elementData = (Object[]) field.get(arrayList);
System.out.println(String.format("現(xiàn)在長度是:%d,現(xiàn)在容量是:%d", arrayList.size(), elementData.length));
arrayList.add("aa");
elementData = (Object[]) field.get(arrayList);
System.out.println(String.format("現(xiàn)在長度是:%d包警,現(xiàn)在容量是:%d", arrayList.size(), elementData.length));
for (int i = 0; i < 10; i++) {
arrayList.add("bb" + i);
}
elementData = (Object[]) field.get(arrayList);
System.out.println(String.format("現(xiàn)在長度是:%d书幕,現(xiàn)在容量是:%d", arrayList.size(), elementData.length));
System.out.println(arrayList);
我們來看結(jié)果:
現(xiàn)在長度是:0,現(xiàn)在容量是:0
現(xiàn)在長度是:1揽趾,現(xiàn)在容量是:10
現(xiàn)在長度是:11,現(xiàn)在容量是:15
[aa, bb0, bb1, bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb9]
源代碼探究
private static final int DEFAULT_CAPACITY = 10; //初始化容量默認(rèn)值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默認(rèn)空集合
// 默認(rèn)構(gòu)造函數(shù)苛骨,初始化為默認(rèn)集合
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 添加方法里面篱瞎,會調(diào)用容量檢查,里面會自動初始化容量
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是一個空列表痒芝,構(gòu)造默認(rèn)容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
我們發(fā)現(xiàn)俐筋,列表默認(rèn)構(gòu)造的時候,采用的是空集合严衬,而調(diào)用add方法的時候澄者,會確認(rèn)容量是否夠用,如果不夠使用,會進(jìn)行擴(kuò)容粱挡,擴(kuò)容的時候赠幕,會進(jìn)行初始化操作,這樣做的好處是询筏,避免初始化的時候榕堰,就浪費(fèi)空間。
我們再來看看擴(kuò)容的源代碼
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新容量采用現(xiàn)有容量+現(xiàn)有容量一半(向右位移1位嫌套,相當(dāng)于除2)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 新擴(kuò)容的容量采用現(xiàn)有容量+現(xiàn)有容量一半(向右位移1位逆屡,相當(dāng)于除2)
- 擴(kuò)容后,對列表進(jìn)行拷貝踱讨,重新建立一個新的列表
結(jié)論
- Java 1.8下魏蔗,列表默認(rèn)初始化的時候,并不會初始化默認(rèn)容量10痹筛,避免初始化浪費(fèi)空間
- 列表不夠用了莺治,按1.5倍進(jìn)行擴(kuò)容,但擴(kuò)容有性能損耗味混,如果能提前估算容量产雹,最好提前設(shè)置