扩容机制(无参)
10个元素以内的容器是怎么变化的?
1.继上述代码进行分析.我们需要搞清楚elementData.length和元素个数之间的关系
elementData.length是这个容器的大小.元素个数就是size;所以咱们回到add方法重新走一遍
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 这个传递过去的是元素添加够的大小.但是实际上还没有添加
elementData[size++] = e; //这行代码才是添加元素的
return true;
}
2.进入ensureCapacityInternal();
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
3.先看看里面的calculateCapacity是做什么的,这个是计算容量的.
这里是使用的无参构造方法创建的ArrayList容器.那么就返回一个容器的默认大小.上边有这个DEFAULT_CAPACITY值为10.
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
4.回到2.进入ensureExplicitCapacity()
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //这个网上说是一个快速报错机制,可能我们看的是同一篇文章
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//这里是扩容的条件.如果元素的个数大于数组的长度,就进行扩容.
//当我们第一add的时候会进行扩容.上面分析过了,会直接扩容到10.但是等我们第二次一直到第10次,都不会进行扩容.
//只有第11次的时候,才会进行扩容因为11 - 10 > 0.现在minCapacity为11
grow(minCapacity);
}
添加第11个元素的时候发生了什么?
可以看一下上面的文字,只有我们的元素要为11的时候才会进行扩容.扩容1.5.倍.到15个大小
5.进入这个grow方法,扩容的核心方法,可以看到扩容并不是在我们要添加的元素的个数的基础上扩容的,而是在容器elementData的长度.
int newCapacity = oldCapacity + (oldCapacity >> 1);
看这个代码,oldCapacity现在是10 ,右移1位,相当于除以2.就是5.再加上原来的10.所以newCapacity为15
基础上进行1.5倍的扩容,调用了Arrays.copyOf在原有的基础上返回了一个新的elementsData;
第一个if是扩容的最小值10,也就是我们第一次调用add的时候会走进去的if.
容器要扩容到Integer.MAX_VALUE +1的时候发生了什么?
第二个if可以看到走进了一个静态方法里面
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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
6.看一下这个静态方法,这个里面涉及到了点其他位置的只是,可能是那个快速报错机制.我猜的,以为毕竟不可能无线扩容.
这个数组的最大值是0x7fffffff.整形就是21亿.01111111 11111111 11111111 11111111.大概就是这么个情况.
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
当我们的newCapacity > MAX_ARRAY_SIZE,但是minCapacity < MAX_ARRAY_SIZE的时候,会将newCapacity扩容为MAX_ARRAY_SIZE.
知道我们的minCapacity > MAX_ARRAY_SIZE的时候才会扩容的int类型的最大值0X7fffffff.
直到我们的minCapacity超过了int类型的最大值.第一位会变成符号位:即
11111111 11111111 11111111 11111111
会变成负数.抛出OutOfMemoryError错误.内存溢出错误.
总结
第一次使用add方法的时候会进行第一次扩容,容器大小为10
直到我们的元素添加到了11个,超过了容器大小10.就扩容大小为当前容器的1.5倍(向下取整)(即15 * 1.5 = 22)
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
然后依次类推一直到要超出容器的大小的时候进行扩容.扩容1.5倍.
当然不是无限大小了.临近最大值的扩容有3种处理
1.扩容后的数组的长度大于int类型的最大值-8,但是实际长度小于int类型的最大值-8.数组会扩容到int类型的最大值-8
2.扩容后的数组的长度大于int类型的最大值-8,但是实际长度大于int类型的最大值-8并且小于等于int类型最大值.会扩容到int类型的最大值
3.扩容后的数组的长度大于int类型的最大值-8,但是实际长度超出了int类型的最大值,会产生内存溢出错误.