结论
- ArrayList通过Object数组来存储元素,自动扩容机制让调用者可直接进行增删改查,而不用关心数组容量问题。
- 扩容机制:
- 容量不够时,进行1.5倍扩容
- 特殊情况:通过无参构造创建ArrayList时,初始Object数组为空数组,容量为0,第一添加元素时,扩容到10
主要变量和构造方法
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
//=========================几个变量===============================
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//内部Object数组,集合元素都保存在这个数组里
transient Object[] elementData;
//当前集合内元素个数
private int size;
//一个空数组
//调用有参构造,手动指定容量为0时,elementData被赋值为它
private static final Object[] EMPTY_ELEMENTDATA = {};
//又一个空数组
//调用无参构造,elementData被赋值为它
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//序列化版本号
private static final long serialVersionUID = 8683452581122892189L;
//=========================主要构造方法===============================
public ArrayList() {
//调用无参构造,内部数组被赋值一个空数组(两个空数组的其中一个)
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
//调用有参构造,指定一个初始容量
if (initialCapacity > 0) {
//容量大于0,内部数组被赋值为指定容量的Object数组。
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//若手指定容量为0,内部数组被赋值为一个空数组(两个空数组的其中一个)。
this.elementData = EMPTY_ELEMENTDATA;
} else {
//初始容量小于0,则抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
}
添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); //每次添加前,都要确保有“size + 1”个容量才行
//如:当前集合内已经有10个元素,要添加一个,最少得确保容量为11
elementData[size++] = e; //将要添加的元素放到数组中
return true; //返回true表示添加成功。
}
确保容量
//传入size + 1,表示需要“size + 1”这么大的容量
//进一步计算最小容量,再确定是否需要扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//无参构造方法中执行过“elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA”
//所以,第一次添加元素时,elementData没有被动过,会走到这
return Math.max(DEFAULT_CAPACITY, minCapacity);
//由此说明,无参构造,第一次添加元素时,最小容量设置为DEFAULT_CAPACITY,即10
}
//若不是无参构造,上面的if判断没用,这个方法也相当于没用
return minCapacity;
}
//计算好了最小容量,看看是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //父类AbstractList中继承过来的一个成员变量,表示表结构修改的次数
//主要是给迭代器使用的,迭代器遍历时,若发现该变量被修改,即结构被修改,则抛出异常
//避免在多线程并发时,其它线程对数组进行了修改,还继续遍历。
//最小容量(所需容量)大于当前数组长度,说明容量不够
if (minCapacity - elementData.length > 0)
grow(minCapacity); //进行真正的扩容操作
//否则,所需容量小于当前数组长度,说明容量够用,方法调用结束,不扩容。
}
//实际扩容
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //获取旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //计算新容量
// ">> 1" ,表示右移一位,表示除以2
//如:0000 0110(十进制6)
//右移一位高位补零 0000 0011(十进制3)
//所以新容量为旧容量的1.5倍
//对新容量进行合法性判断
if (newCapacity - minCapacity < 0) //新容量比所需容量小???
newCapacity = minCapacity; //那怎么行,起码也要设置为所需容量才行
//其实主要发生在第一添加元素时,
//此时elementData.length 为 0
//所以,计算出来的newCapacity也为0
//当然还有一些其他情况,比如设置了初始容量为1
//计算出来的newCapacity为1,显然不合适
if (newCapacity - MAX_ARRAY_SIZE > 0) //新容量比规定的最大值(MAX_ARRAY_SIZE)要大
newCapacity = hugeCapacity(minCapacity); //通过hugeCapacity方法给一个合法的新容量
//旧容量太大时,比如超过规定最大容量的一半
//此时,1.5倍扩大后,肯定超过了规定的最大值
//到此,新容量没问题,调用Arrays.copyOf进行扩容
//开辟一个大小为newCapacity的数组,将elementData中的数据挨个复制过去
elementData = Arrays.copyOf(elementData, newCapacity);
}