Java中的List集合无法初始化容量
我们都知道Java的List集合是有进行一个懒加载的优化的,当你没有在集合里面存放元素的时候,集合的size为0
可我记得List集合是可以指定大小的,于是我写了一段代码去进行测试
我这个时候就非常疑惑,我不是指定了大小吗?为什么size还是0?
想不明白,我就开始翻源码看看
原来size是记录的集合里面的元素个数,而不是集合的容量大小
-
先看空参构造器
从这段代码我们可以看得出懒加载机制,当没有往集合中放任何东西 的时候,就会初始化一个{}
java
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 再看看add()源码
java
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 添加到集合之前先把判断一下当前容量size + 1是否会超出容量大小
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 这里开始判断了,如果说你是第一次初始化,即集合为{}的话,我就会给你分配 DEFAULT_CAPACITY = 10
// 需要注意的是这里并没有把 size的值设置为10,size只记录集合里面有多少元素,而不是集合容量
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity; // 如果说你不是第一次添加了,就返回当前的容量 + 1的值
// 这个地方有点绕,需要看传参 add方法里面的 ensureCapacityInternal(size + 1) 这个size + 1 就是minCapacity
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 这里的modCount先不用管,因为ArrayList线程不安全,当modCount和expectModCount不一致就会抛异常
// 当这个minCapacity大于数组长度的时候,也就意味着数组放不下了,这个时候就需要扩容了
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 从源码我们可以看出,ArrayList集合底层的扩容机制就是通过数组的复制
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 说点题外话,ArrayList集合是如何扩容的,拆分一下grow源码
java
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
这里可以看到newCapacity会把你原来集合的大小加上你原来集合大小的一半来进行扩容,就是1.5倍扩容,这是第一次
第二次扩容是什么时候呢?
java
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
当第一次扩容结束后,发现还是放不下,就会按照你给的集合大小来扩容了
当发现扩容后的大小超出了 MAX_ARRAY_SIZE 就会把整数的最大值给你了 Integer.MAX_VALUE
java
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
总结
经过这次奇怪的尝试,终于搞清楚了size到底是什么了
我如果想要初始化List集合,就不能初始化集合容量 ,而是初始化集合的size
也就是说,如果想要真正的初始化这个size,就必须要往集合里面添加元素才能改变这个size属性