ArrayList是我们在程序日常开发中用到的最常用的类。本文是基于Java8对源码进行分析,java11的源码有些许变化,但是大同小异,下面我们来看看它的源码。
1.首先,elementData是ArrayList实际保存对象的地方,是一个Object的数组,transient关键字表示这个数组不进行序列化。
2.构造器,默认的构造器会将一个上面一个默认的空数组被引用到我们的element上,这时候element是一个size为0的空数组,到触发了add方法,进行扩容操作。
除了默认的构造方法,还有一个可以声明初始数组大小的构造器。
我们还可以传入一个其它实现了Collection接口的入参,将这个Collection的值通过Arrays.copy方法复制到一个新的数组中。
Arrays.copy方法:入参分别是 original-原数组,新数组的长度,新数组的类型。
((Object)newType == (Object)Object[].class)/
//先判断传来的新数组类型和Object数组类型是否是内存中的同样一块,因为Class在虚拟机中只保存一块,如果类型相同,那么他们的Claas会==
如果是Object类型那么直接声明一个Object的新数组,如果不同那么调用底层的Array.newInstance方法生成一个新类型的数组。
注意:初生成的新数组,值都是null,需要使用
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
将原数组复制到新的数组中。
3.核心的add方法,在每次往ArrayList新增元素时,都会进行一个判断elmentData数组的大小进行扩容操作,三种情况:
空数组:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY ==10
}
return minCapacity;
}
往空数组里第一次插入数据,会返回一个默认的数组大小10,然后10-原数组长度>0时,执行grow方法,第一次插入,
int oldCapacity = elementData.length;//第一次是0
int newCapacity = oldCapacity + (oldCapacity >> 1);//0+0/2=0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//那么第一次插入的默认数组长度就是10了
第二次扩容:往数组插入第11个数时,
int oldCapacity = elementData.length;//这次原长度为10
int newCapacity = oldCapacity + (oldCapacity >> 1);//10+10/2=15
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//那么第一次插入的默认数组长度就是10了
elementData = Arrays.copyOf(elementData, newCapacity);
每次扩容,新的数组长度都是之前的1.5倍