一,创建一个ArrayList对象时,如果没有给它附上参数,那么ArrayList底层源码会将这个集合的大小默认为0,当我们给这个集合添加元素时,初次添加元素时,底层的源码会给集合大小从0扩大到10的容量,再次添加元素时,底层的源码会比较集合元素的长度和集合的大小,如果发现集合大小不够添加新的元素,则会按原来的集合大小的1.5倍扩容,也就是变到15.
无参ArrayList底层源码分析:
如上图,我们创建一个ArrayList对象并遍历填充,一开始的时候是调用无参构造器的,此时的集合大小为0。
上图是执行遍历第一个i填充,也就是list.add(i) //i==1的时候,进到里面代码,需要先将基本数型类型int的i装箱成Integer类型。
上图是添加第一个i==1时,进入到add底层代码,modCound是记录多少次访问,主要的方法还是下一个add方法,进入到add(e,elementData,Size)方法看看。
此时的e=我们要添加的值,这个值的类型可以是任意类型,为Object,elementData就是集合的容量大小,size就是在添加这个元素之前,集合中已经有多少元素了,因为是第一次添加,所以size=0,而这个elementData我们上面也有讲过,初次调用无参ArrayList时,底层源码会给它自动设置为0,所以这个if条件判断语句是成立的,s=0,elementData.length=0,所以要调用grow()方法,现在进去grow方法里面看看。
minCapacity就是集合中最小要有多少容量大小,这个size就是集合中已有的元素个数,加1是因为我们要新添加一个元素,所以要在原来的基础上,集合大小要至少多1位,然后调用有参grow方法 。进去看看
传进来的这个minCapacity,先看第一条代码,将集合容量大小赋给oldCapacity,此时的集合容量大小为0,所以oldCapacity是等于0的。
再往下看if语句,判断条件是不成立的,因为oldCapacity=0,elementData也等于0,这个DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是初次容量大小,为0,以此是相等的,又这个if语句不成立,执行else语句,minCapacity前面说为1,DEFAULT_CAPACITY是为10,1和10中取最大值,自然为10,创建容量大小为10的Object数组赋给elementData,前面有说过,elemenData就是集合的对象,赋给集合的对象,因此到这儿,集合的大小就从0扩大到10了,集合的本质是一个Object类型的数组。
这就是我们说的,一开始调用无参ArrayList对象时,集合容量为0,当要添加元素时,集合容量扩大到10的原因。这样,我们就可以往这个集合中添加10个Object类型的值了。
问题二,当集合中的容量为10,而此时集合中已经又10个对象,需要新添加对象时,集合的容量为什么是按1.5倍扩容的?
这种情况下,前面的底层源码基本和上面讲过的类似,最终还是要调用grow()方法扩容,不同的是,此时的oldCapacity是10,大于0,所以下图这个if条件语句是可以执行的,往下看条件语句中的内容,newCapacity就是创建的新的容量集合,使用ArraysSupport.newLength()有参方法,括号中有三个参数,第一参数不变,需要从第二个参数中选择一个较大的参数和第一个参数相加,并赋给这个newCapacity,此时的minCapacity就是11,oldCapacity为10,相减就为1,后面的oldCapacity>>1的作用就是oldCapacity除以2,也就是5,5>1,因此最终的结果就是10+5=15赋给newCapacity,然后调用Arrays.copyOf方法,这个方法的作用是把前面较小的集合内容覆盖到后面这个新创建的,没有一个元素的较大的集合中,这样就可以方便新创建的较大的集合中有之前已经添加进去的原来的元素,只是这个集合更大,能融入更多的元素,执行完之后再返回。以上就完成了集合ArrayList的扩容,此后的每次扩容,都是前面的1.5倍,原理如上所述。至此分析完毕。
二,那问题来了,如果是有参数的ArrayList对象创建呢?这个时候底层源码又会给这个集合怎么扩容,怎么分配集合大小呢?让我们来分析分析
如果是有参数的集合对象,底层的源码就会给这个集合创建一个给定参数大小的Object数组赋给这个集合,之后如果集合大小满了,扩容的倍数还是按照1.5倍扩容。
分析分析源码:
当创建一个比如List list=new ArraysList(8);的集合对象时,进入这个对象的里面可以看到下图所示的方法,这个方法就会执行第一个if条件语句,initialCapacity就是我们传进来的参数大小8,if内就会创建一个大小为8的Object数组赋给这个this.elementData,这个elementData就是集合的对象,赋给它一个大小为8的Object数组,就是创建了一个容量为8的集合。
之后再往里面添加元素,如下图,因为s是集合中已有的元素个数,elementData.length就是集合的容量,如果还没有填满这个集合容量之前,是不会执行这个if条件语句的内容的,而是直接调用elementData[s]=e,添加这个新的元素,如果满了,就进入if语句执行grow()方法。
进入到grow方法内,,oldCapacity等于集合的容量,也就是8,满足大于0的if条件,就会进入到if内执行if中的代码,下面的就不用说了,前面讲过了,具体的作用就是把现有的集合大小扩大到原来的1.5倍,也就是8+4=12,所以新的集合容量大小为12,后续如果集合再次满了,需要继续扩容,也是按照相同的方法来进行。