首先,ArrayList集合存储的数据在底层是一个数组(名字elementData),这个数组是Object的数组,因为是Object数组,所以集合啥都可以装。
讲解ArrayList的扩容机制,要从ArrayList的构造器来分类,ArrayList有两种构造器,第一种是无参构造器,调用无参构造器创建的对象,在底层elementData数组是一个长度为0的空数组;第二种是有参构造,传入的是initialCapacity(初始的elementData数组的长度),调用这个构造器创建的对象在底层elementData数组的初始长度就是你传入的长度。
首先来演示调用无参构造的情况:
debug进入源码:
第一步:进入无参构造器
可以看到,无参构造将
DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给了elementData数组,那
DEFAULTCAPACITY_EMPTY_ELEMENTDATA是什么呢?
再追进去看,可以看到
DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一个长度为0的空数组,数组里面装的是Object的东西
再来看看调用有参构造的情况:
追进 源码可以看到,首先进行判断你传进来的数据是不是大于0的,大于0就new一个长度为你传进来的数据(initialCapacity)的数组赋给elementData数组;
如果你传进来的数据是0,则将
EMPTY_ELEMENTDATA赋给elementData数组,那
EMPTY_ELEMENTDATA又是什么呢?在追源码,可以看到它是一个长度为0的数组
如果你传进来的的数据小于0,那就会抛出一个异常
下面开始进入ArrayList集合的扩容:
第一种情况:用无参构造创建的对象,并且还没有对这个对象的数据进行操作,是首次对这个对象进行数据的操作
我们来add一个数据
debug进入源码:
这里的size是一个int类型的数据,指的是elementData数组里面实际存在的元素的个数(不是数组的长度),比如我一个长度为10的数组,长度是在定义数组的时候就确定的,但是如果我一个数据都没存放,那size就是0,放一个进去,size就是1;
理解了size的意义,再来下一步,
进入
ensureCapacityInternal()这个函数
这个函数的参数minCapacity就是刚才的(size + 1),这个函数里面又调用了
ensureExplicitCapacity这个函数,
ensureExplicitCapacity这个函数的参数又是
calculateCapacity这个函数的返回值
那我们先来看
calculateCapacity这个函数
这个函数先判断elementData数组是否是一个长度为0的数组,如果是,就返回
DEFAULT_CAPACITY(一个int型属性 等于 10)和minCapacity之中的最大的那个,其实永远也只会返回DEFAULT_CAPACITY,因为当elementData长度为0时,这个数组的size不就是0吗,minCapacity就是传进来的size+ 1 = 1,不永远小于DEFAULT_CAPACITY吗?
好,现在
ensureExplicitCapacity函数接收到了10这个形参
进入这个函数,minCapacity就是传入的10,
modCount++不用管,它与扩容无关
下一步
判断minCapacity(10)是否大于elementData这个数组的长度,此时这个数组是长度为0的数组,
那当然满足这个条件,进入grow函数,grow函数才就是真正的给elementData数组扩容的函数,
grow函数比较复杂,但功能很简单,(1).如果elementData数组是长度为0的数组(也就是用无参构造创建的对象),就将elementData扩容至长度为10 (2).elementData数组长度不为0(给elementData数组已经装满了,要想再装数据就得扩容),此时的elementData数组就会扩容至原elementData数组长度的1.5倍
附加说明:
其实这个函数的if()里面的语句作用就是用来给无参构造创建的对象的elementData数组进行初始化的,初始化长度为10,也就是说,只要是使用无参构造创建的对象,elementData数组的长度都是10,使用有参构造创建的对象的elementData数组的长度就是你指定的长度(0除外,0是10)
每次进行调用add进行数据的添加,都会进行判断elementData数组的长度还够不够加进去1个数据,如果不够就会进行1.5被的扩容,再将数据加入到elementData数组中,同时size++;