ArrayList类的成员变量
//默认的数组容量
private static final int DEFAULT_CAPACITY = 10;
//一个空的Object数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//一个空的Object数组,同上面的区别是该变量只用于无参构造,表示ArrayList对象刚执行完空参构造,需要与上边的变量区分
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//实际存储数据的数组,ArrayList的底层实现就是Object数组
transient Object[] elementData;
//elementData已使用的大小
private int size;
ArrayList的构造方法
//调用空参构造时,elementData初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//传递int类型参数时,若initialCapacity>0 则 elementData初始化为指定大小的Object数组(大小为initialCapacity)。
//若initialCapacity==0,则elementData初始化为EMPTY_ELEMENTDATA,表示初始化时指定了大小为0。
//若initialCapacity<0,则抛出非法参数异常。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
需要注意的地方是,在初始化过程中,size变量没有赋值,采用的是int类型初始化时的默认值。
add方法
public boolean add(E e) {
//将扩容之后的数组大小作为参数传入
ensureCapacityInternal(size + 1);
//将e放入数组末尾之后,自增
elementData[size++] = e;
//返回true,表示插入成功
return true;
}
方法内容很简单
- 确保容量够用,其中ensureCapacityInternal方法是扩容机制的重中之重,核心部分就在于此。
- 将传入的参数赋值到elementData的末尾,size自增
- 返回true,表示元素增加成功
ensureCapacityInternal(int minCapacity)方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
该方法中首先调用 calculateCapacity计算最小容量
//若elementData是调用空参构造进行初始化,则返回DEFAULT_CAPACITY 和minCapacity中的较大值。DEFAULT_CAPACITY为10。
//否则表示elementData在初始化时指定了数组大小,或者其中已经有值。直接返回minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
计算得到当前数组的最小容量之后,将其作为参数调用ensureExplicitCapacity(minCapacity)方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //该变量记录的是list对象的修改次数,add,remove方法都会是该参数自增。
//当插入数据之后的数组容量比elementData声明时的大小要大时,需要对数组进行扩容,调用grow方法进行数组的赋值
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
该方法中的逻辑也相当简单
首先modCount自增,这个变量用于记录list的写操作次数,调用add或者remove方法时都会自增。这个字段的具体作用可以参考
https://www.cnblogs.com/zuochengsi-9/p/7050351.html 这篇文章中的解析比较详细,此处不再赘述。
然后比较插入数据之后的数组大小,和elementData声明时的数组长度(不是已使用的长度,需要区分开),若minCapacity大于数组声明长度时,表示数组目前数组已经装满,想要再往里塞一个数据,就需要对数组进行扩容,调用grow(minCapacity)方法。
这个方法是自动扩容的核心实现,需要深刻理解。那我们再来看下grow(minCapacity)方法中都干了些什么
private void grow(int minCapacity) {
// 根据elementData的声明长度计算新数组的长度,计算方法为 原数组长度+原数组长度的一半。使用移位操作可以加快计算速度
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果若扩容后的长度还是比minCapacity效的话,则将minCapacity作为新数组的长度,这个情况只发生在数组长度为0时,计算出的newCapacity为0,并且minCapacity取用的是数组默认大小10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若新数组长度比MAX_ARRAY_SIZE要大时,重新计算新数组长度,根据minCapacity计算,minCapacity大于MAX_ARRAY_SIZE时返回Integer.MAX_VALUE,否则返回Integer.MAX_VALUE-8作为新数组长度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//对elementData进行复制,复制的长度为新数组长度。
elementData = Arrays.copyOf(elementData, newCapacity);
}
所以,在我们创建ArrayList对象时,可以有意识地调用有参构造,减少第一次操作时的自动扩容步骤