private static final Object[] EMPTY_ELEMENTDATA = new Object[0]; //1
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0]; //2
transient Object[] elementData; //注意:这里elementData的大小指的是容量的大小,而不是实际存储的大小
public ArrayList(int var1) { //7
if (var1 > 0) {
this.elementData = new Object[var1]; //3
} else {
if (var1 != 0) { //4
throw new IllegalArgumentException("Illegal Capacity: " + var1);
}
this.elementData = EMPTY_ELEMENTDATA; //5
}
}
public ArrayList() { //8
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //6
}
从ArrayList的带整型参数初始化方法(注释7)中,可以看到,当传入参数是大于0的数字,将会创建var1大小的数组(3)。
由此也可以看出:ArrayList底层是基于数组实现的。
当var1的值不等于0(4),则说明输入的是不合法的值,因此应该报IllegalArgumentException
非法参数异常。
如果输入的容量值var1是0,那么值为EMPTY_ELEMENTDATA(2),即创建一个空数组。
如果创建ArrayList的时候采用的是无参构造方法(8),那么在初始化的时候,容量大小为DEFAULTCAPACITY_EMPTY_ELEMENTDATA(2),创建的也是一个长度为0的数组。
由此我们可以得出:ArrayList初始化的容量大小是0.
接下来查看add方法:
public boolean add(E var1) {
this.ensureCapacityInternal(this.size + 1); //1
this.elementData[this.size++] = var1;
return true;
}
private void ensureCapacityInternal(int var1) {
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //2
var1 = Math.max(10, var1);
}
this.ensureExplicitCapacity(var1); //3
}
在add的方法我们可以看到第一行语句(1),调用了ensureCapacityInternal方法,在该方法中我们可以看到第一行语句(2)就是判断当前容量(elementData )是否是默认值,如果是默认值,那么比较var1(传入的参数,为当前size+1的值)与10的大小。如果当前数组长度小于10,var1的值将为10。使用var1的值究竟要干嘛呢?我们继续追踪下去(3):
private void ensureExplicitCapacity(int var1) {
++this.modCount;
if (var1 - this.elementData.length > 0) { //3
this.grow(var1); //1
}
}
private void grow(int var1) {
int var2 = this.elementData.length;
int var3 = var2 + (var2 >> 1); //3
if (var3 - var1 < 0) { //4
var3 = var1;
}
if (var3 - 2147483639 > 0) { //主要是对当前容量大小进行限制,避免超过限制大小。
var3 = hugeCapacity(var1);
}
this.elementData = Arrays.copyOf(this.elementData, var3); //2
}
通过追踪ensureExplicitCapacity方法,我们可以看到在方法里(3)先判断了当前需要的容量大小(var1)是否大于原容量大小(elementData.length),只有当前容量大小无法满足所需容量,才会进入grow方法进行扩容。它把var1的值传入了grow方法(1)。
进入grow方法,首先var2的值为当前容量的大小,在注释3的位置,var3=当前容量的大小(var2)+当前容量的一半(>>:右移运算符,在保证没有溢出的情况下,右移n位,相当于除以2的n次方)。
由此我们可以得出:ArrayList每次进行扩容时,都是扩大到原来的1.5倍。
var3则为我们扩容后的容量大小,但当第一次添加元素的时候,elementData.length值为0,因此会出现var3为0的情况。所以我们使用注释4处的判断,来避免这一情况,使第一次添加元素时,ArrayList扩容的值保持原值10。
接着,我们可以看到注释2处的语句,使用了Arrays.copyOf
方法,那么这个方法有什么用呢?我们进行测试一下:
可以看到使用这个方法,会利用原本的数组,拷贝一个自定义长度(传入的第二个参数值)的数组。如果定义的长度小于原数组长度,那么就截取原数组;如果大于,则就用0扩充。因此我们可以知道,ArraryList在扩容时会重新创建一个数组,将原来的数据复制过去,然后数组指向新的地址,这是十分损耗性能的。
至此我们知道了,var1被传入到了grow方法中,完成了数组的扩充。在前面已经知道当ArrayList的容量小于10时,var1传入的值为10。
由此我们可以得出:ArrayList初始化的容量大小为0,只有在第一次调用add方法的时候,默认创建容量10的空间。如果还需要扩容,则扩充到原来的1.5倍。