我们会从ArrayList的源码进行讲起,一点点的解析各种细节,首先我们要在idea中创建一个ArrayList的集合,调用它的add方法,然后摁住CTRL键,鼠标点击add方法查看其中的源码部分。
package collection;import java.util.ArrayList;public class ArrayListTest {public static void main(String[] args) {ArrayList<Integer> list=new ArrayList<>();list.add(1);}
}
一,add
首先我们一点一点开始说,创建完成后我们点进add方法的源码中查看
public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true;}
这部分就是add方法中的源码,我们先了解一下方法中的形式参数以及变量是什么以及的作用是什么,首先我们知道ArrayList的底层数据结构为Object数组,而这个elementData就是那个Object数组,因为刚刚创建的缘故,这个Object数组是一个长度为0的空数组,这个size属性是代表list集合的元素的数量,刚刚进入add函数后,要调用ensureCapacityInternal这个方法,这个方法的作用是确定内部的容量,它传入的参数是list集合原来的长度+1,这是因为添加元素所以添加后的容量就为size+1了,我们接着点击进ensureCapacityInternal这个方法中
二,ensureCapacityInternal
private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}
在这个方法中我们的size+1就是minCapacity代表这最小容量,这里我们一层一层的看,先是调用了calulateCapacity方法,它的作用是预测容量,传入的参数是我们的Object数组,和minCapacity这里我们再点进calculateCapacity中
三,calculateCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}
到了这里就开始出现逻辑了,首先进入后会判断底层数组是否为刚刚创建的空数组,这里面的DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是刚刚创建的空数组,如果是返回默认容量DEFAULT_CAPACITY和minCapacity的最大值,这里的默认容量为10,如果不是则直接返回minCapacity,因为我们刚刚创建这个集合,这里的elementData就是刚刚创建的空数组,并且我们的数组长度还为0,所以这里我们直接返回10了,这里的calulateCapacity的返回值就是我们所预期的容量了,得到了返回值,再将返回值给ensureExplicitCapacity方法,这个方法的作用是确定显示容量,传入的参数就是我们预期的容量了,我们点进这个ensureExplicitCapacity方法中去
四,ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}
这个方法就是用于给数组扩容的,首先既然已经执行这个方法了,说明你调用了add方法,你就是对这个集合进行了修改,所以这里的,modCount进行自增,它是修改次数,这个属性对扩容没有什么影响,所以就先不说有什么用了,然后进行一个判断看看我们所预期的长度是否有我们现有的Object数组的长度长,如果有那么进行扩容,没有就说明数组足够长,不需要进行扩容,我们刚刚创建了一个集合所以数组长度为0,所以肯定没有我们预期的10大,所以要调用grow方法,将我们需要扩容的长度传入grow方法中,之后点进grow方法中去。
五,grow
private void grow(int minCapacity) {// overflow-conscious codeint 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);}
在grow方法中,会先后声明两个变量,一个是旧容量是我们的原来的数组长度,一个是新容量,这里的右移一位就是‘/2’的意思,如果旧容量是偶数,那么新容量就是旧容量的1.5倍,如果是奇数,那就为旧容量+旧容量/2(向下取整),也是很接近旧容量的1.5倍的,然后再进行判断,看看新容量的值是否要小于所预期的值,因为这里如果旧容量要是0,那么新容量还是0,根本没有增加,所以进行了一次防0判断,如果小于那么新容量就直接变为预期容量,如果我们的预期容量要大于新容量那就ok,原则就是你容量可以比我预期的大,但是绝不能比我小,之后的那个判断会很少用,它是用来处理数组长度大于Integer的最大值-8时出现的问题的,会进入这个hugeCapacity方法
六,hugeCapacity
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}
这个方法了解就行,如果你的预期容量要是小于零,就是溢出了,那么直接返回内存不足的错误,如果说你的预期容量要是大于这个Integer的最大值-8那么就直接将你的这个新容量赋值为Integer的最大值如果不大于那么就赋值为Integer的最大值-8。
我们得到了新容量之后就调用Arrays,copyOf方法用来创建一个数组长度为新容量的一个底层数组,再将原数组的值按位填充进去,最后回到add方法中elementData[size++] = e;进行这段代码size自增,将元素放在数组的尾部,即完成了扩容的全部过程。
七,总结
就是刚刚创建集合的时候底层数组是没有长度的,但是你添加一个值后底层数组就变成10了,然后等你元素的个数大于10的时候再次进行扩容为原来的1.5倍,以此类推。
这是我自己理解的,有些地方可能不太严谨,请大家多包容多指正谢谢。