上一篇中我们了解到,ArrayList底层是基于数组实现的,并且通过add()方法实现元素的增加。
全网最详细的--ArrayList与LinkedList的区别与耗时对比_Petter's Blog的博客-CSDN博客
但是我们知道数组初始化时都是指定元素个数,或者指定数组大小,而ArrayList既然是基于数组实现的,为什么没有初始化大小,也没有指定元素,也可以用呢???
/*数组的初始化方式*/
int[] arrays = new int[3]; //第一种
int[] arrays1 = {1,2,3}; //第二种
而且为什么通过add()方法就可以往里面增加元素呢???
以往的数组可是指定了大小,就不可以再变了,ArrayList是怎么实现动态的改变数组大小呢???
而且为什么可以一直往里面add,ArrayList没有边界吗???
一、ArrayList的初始化
第一种构造方法
List<String> list = new ArrayList<>(); //无参的构造方法
ArrayList中有一个默认容量,默认在初始化时的容量大小为10,并且调用了一个无参的构造函数,将“数组”内的元素赋值。
private static final int DEFAULT_CAPACITY = 10; //默认初始大小为10
transient Object[] elementData; //ArrayList中的元素集合
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //默认置为空
}
第二种构造方法
List<String> list = new ArrayList<>(2); //指定容量大小
指定容量大小进行初始化,如果大于0,则分配一个对应大小的Object数组;如果小于0,则分配一个空数组,相当于无参的构造函数;除此之外,抛出异常。
private static final Object[] EMPTY_ELEMENTDATA = {}; //空数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { //如果初始容量大于0,则分配一个对应大小的Object的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;//如果等于0,则等于一个空数组
} else { //否则报错
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
通过以上两种构造方法,实现了ArrayList的初始化,即指定数组容量大小,将数组元素值设置为空数组。
二、add()方法的实现
1、将数组大小加1
2、将元素赋值到最后一位数组下标上
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 修改数据大小,+1
elementData[size++] = e; //将最后一个下标赋值为新增元素
return true;
}
三、动态扩容
修改数组大小前,先对容量大小进行校验,如果新增前,是个空数组,则还是采用默认容量大小:10,如果数组内已经有值,则采用 (当前容量+1) 后的容量大小。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果新增前,是个空数组,则还是采用默认容量大小:10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
grow()为ArrayList的扩容方法,在扩容前,首先判断当前容量大小是否已经超出限额。比如初始化容量大小为10,当新增第二个元素时,minCapacity为3,这是3-10 < 0,则不进行扩容,还维持初始化的容量;否则,则需要扩容。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //如果新增后的容量小于限额容量,则不进行扩容
grow(minCapacity); //否则扩容
}
扩容大小默认为当前容量的1.5倍。
比如当前容量大小为10,则新的容量大小:10 + (10>>1) = 15。然后将原来的10个元素、以及新增的第11个元素放入到这15个空间中。
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //扩容默认为当前容量的1.5倍
//如果扩容后的容量大于所需要的容量,则扩容完成,之后进行最后一步的拷贝
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //如果扩容之后的容量小于当前容量,则不进行扩容(用于数组为空时,第一次add元素)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
当然,ArrayList的最大容量也有一个限额,当超过默认的最大容量后,则会抛出异常。
如果超出最大容量2的31次方,则会变为负数,并抛出异常
//ArrayList的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//判断是否超出最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//如果超出最大容量2的31次方,则会变为负数,并抛出异常
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError(); //抛出异常
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}