我们在使用ArrayList的时候,了解到默认初始化容量是10,在Java 1.8的情况下,我们看下真实情况如何。我们先看一个Demo代码,执行步骤如下:
- 初始化一个列表,查看容量
- 向列表添加一个元素,查看容量
- 向列表添加十个元素,查看容量
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("现在长度是:%d,现在容量是:%d", arrayList.size(), elementData.length));
arrayList.add("aa");
elementData = (Object[]) field.get(arrayList);
System.out.println(String.format("现在长度是:%d,现在容量是:%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("现在长度是:%d,现在容量是:%d", arrayList.size(), elementData.length));
System.out.println(arrayList);
我们来看结果:
现在长度是:0,现在容量是:0
现在长度是:1,现在容量是:10
现在长度是:11,现在容量是:15
[aa, bb0, bb1, bb2, bb3, bb4, bb5, bb6, bb7, bb8, bb9]
源代码探究
private static final int DEFAULT_CAPACITY = 10; //初始化容量默认值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默认空集合
// 默认构造函数,初始化为默认集合
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 添加方法里面,会调用容量检查,里面会自动初始化容量
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) {
// 如果是一个空列表,构造默认容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
我们发现,列表默认构造的时候,采用的是空集合,而调用add方法的时候,会确认容量是否够用,如果不够使用,会进行扩容,扩容的时候,会进行初始化操作,这样做的好处是,避免初始化的时候,就浪费空间。
我们再来看看扩容的源代码
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新容量采用现有容量+现有容量一半(向右位移1位,相当于除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);
}
- 新扩容的容量采用现有容量+现有容量一半(向右位移1位,相当于除2)
- 扩容后,对列表进行拷贝,重新建立一个新的列表
结论
- Java 1.8下,列表默认初始化的时候,并不会初始化默认容量10,避免初始化浪费空间
- 列表不够用了,按1.5倍进行扩容,但扩容有性能损耗,如果能提前估算容量,最好提前设置