集合就是给我们一种更丰富的更灵活的对于数据的存储和操作的一种形式,ArrayList内部就是根据数组来存储的,我们知道数组的存储是受长度限制的,但是我们永远不用关心ArrayList内部数组的长度,因为它会自己来动态的维护整个数组中的容量的大小,也就是我们常说的扩容。
首先我们先来了解一下ArrayList的数据结构
ArrayList的数据结构
ArrayList基于Object[]数组实现,Object[] elementData
ArrayList的扩容方式
ArrayList的扩容方式根据初始化的不同而不同
初始化又分为: 无参构造方法
有参构造方法
(1)无参构造方法的初始化 :
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
如上图所示,无参构造方法会将内部数组初始化为一个长度为0的空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),空数组是不能往里面存东西的,那么我们就要对此进行改变,那么就要进行扩容
我们先通过无参构造方法创建一个ArrayList,然后在其中加入元素:
import java.util.ArrayList;
public class IS_ArrayList {
public static void main(String[] args) {
// 无参构造方法
ArrayList<String> list=new ArrayList<String>();//内部数组默认初始化为空数组
list.add("AAA");//第一次扩容为10
}
}
从上面我们已经知道无参构造方法初始化会将内部数组初始化为一个长度为0的空数组,然后我们通过add()方法向数组中加入元素,
add()方法为:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确保内部的容量够用
elementData[size++] = e;//这一行数组才是真正把元素放到elementData数组中的方法,
//如果是一个空数组,或者数组已经满了的话,那自然是不能放进去的,
//那么首先就要确认数组的容量够用
return true;
}
ensureCapacityInternal(size + 1):size是当前数组中的元素个数,+1就是看现在我们要在里面再加一个元素看他的容量够不够
- 在上图源码中我们可以看到:ensureCapacityInternal中其实就是将数组所需要的最小长度minCapacity(也就是size+1)传入calculateCapacity方法中
- 在calculateCapacity方法中,如果我们从头开始看,即从数组的初始化开始,那么刚开始的时候数组的长度为0,size+1就是1,如果elementData等于刚开始我们初始化的那个常量,那就意味着我们当前对于这个集合里面的数组的访问是第一次,意味着这个数组里面没有任何元素的添加,这个时候返回一个最大值(你所需要的最小容量和DEFAULT_CAPACITY常量之间返回一个最大值,谁大以谁为准)DEFAULT_CAPACITY为10,刚开始时最小容量为1,显而易见两者相比10大,那么返回的容量大小就为10,可得我们第一次计算的长度容量大小就为10,第二次扩容数组长度不等于空了,最小长度变为了(10+1=11)接下来我们就把11传递给了ensureExplicitCapacity()
- 在ensureExplicitCapacity中如果最小长度大于数组的长度就要进行扩容grow(minCapacity),第二次扩容时11-10>0就要执行grow()
下图是grow()的源码:
在grow()中我们先让数组的长度等于oldCapacity,新数组的长度就是老数组长度再加老数组长度的一半,即新数组长度是老数组长度的1.5倍,(比如第二次扩容时就是10+5=15)
elementData = Arrays.copyOf(elementData, newCapacity);
把elementData这个数组里面的元素复制到新数组里面,复制的个数就是新的容量个数(比如第二次就是15),这样既完成了数组容量的扩大,还把旧的元素复制过来了(这就是真正意义上的扩容)(当容量不足时,按照原容量的1.5倍进行扩容增长)这就是通过无参构造方法进行扩容的理解。
(2)有参构造方法的初始化 :
比如我们创造ArrayList集合的时候给它一个参数200,即初始化它的数组长度就为200
当 initialCapacity > 0:按照初始化容量准备elementData这个数组
当 initialCapacity == 0:让它变成一个空数组
当 initialCapacity 不大于0,也不小于0,那么就是一个负数,就会抛异常
如果我们可以预估我们要创造的这个集合的规模的话那么这个数组的初始化就到位,那么前200个就不需要扩容,如果所加入的数组超过200的话再进行扩容,这样就会提高集合性能