ArrayList源码解析,底层结构+扩容机制,通俗易懂

1.4ArrayList源码解析

1.4.1.无参构造器创建ArrayList

1.首先创建一个ArrayList,并采用无参构造器进行初始化对象。
在这里插入图片描述

当我们在第12行打一个断点追溯进去,无参构造器当中的代码为:this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
在这里插入图片描述

向上翻阅源码,会发现其实ArrayList底层就是一个Object类型的数组elementData,来存放我们add进去的元素,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一个空数组,当我们使用无参构造器创建ArrayList对象的时候,就会初始化elementData为空的数组。

在这里插入图片描述

2.执行list.add(“a”),我们一起追溯进去看看ArrayList到底是怎么样进行大小扩容的。

在这里插入图片描述

当执行list.add()方法的时候,发现会执行ensureCapacityInternal方法,并且传入的参数为size+1,size表示的是ArrayList的长度,刚开始的size值为0,为了能让他执行当前的add方法,则需要给他先加上一个长度,才能添加当前的这个元素。

在这里插入图片描述

接着我们继续step into ensureCapacityInternal这个方法,接着step into calculateCapacity()方法,calculateCapacity方法传入的参数为数组以及当前所需最小容量。

在这里插入图片描述

进入到方法以后,会先坐一个判断,如果当前elementData数组为null,则返回DEFAULT_CAPACITY(10)和 minCapacity当中的最大值,则也说明了当我们使用无参构造器创建ArrayList对象的时候,第一次扩容的大小为10,默认我们第一次就需要10个大小。

在这里插入图片描述

接着退出calculateCapacity()方法,进入ensureExplicitCapacity()方法,有一个modCount变量记录着我们对list集合进行了几次操作。接着会执行第238行的if语句,我们的minCapacity为刚刚方法的返回值10,而elementData.length为0,所以会执行grow()方法。
在这里插入图片描述

接着step into grow()方法,这个方法也可以算是扩容的核心算法了,首先将当前数组的长度赋值给oldCapacity,接着执行259行,将1.5倍的旧容量赋值给新的容量,这也就是我们所知道的ArrayList第二次进行扩容的时候,是第一次的1.5倍大小

在这里插入图片描述

但其实当第一次进入grow()方法的时候,old和new都为0,所以260行的if语句为true,会把第一次扩容的10赋给newCapacity

在这里插入图片描述

接着再执行第265行Arrays.copy(),将原本的elementData 复制赋值回去,通过debug可以看出此时的elementData数组为10个null值。
在这里插入图片描述

接着会继续执行add方法剩下的语句,也就是将元素添加进数组当中。

在这里插入图片描述

此时list集合当中已经有一个a,这个时候程序会继续执行add(b)。

在这里插入图片描述

后面就是重复的步骤,当list添加了10个元素以后,就会继续扩容,将newCapacity赋值为oldCapacity的1.5倍,也就是第二次扩容会有15个空间。这边就不再继续演示源码过程,直接贴上添加第十一个元素的时候,list集合的情况。

在这里插入图片描述

1.4.2.有参构造器创建ArrayList

上面演示的是通过无参构造器进行集合的创建。还有另一种方法,就是通过有参构造器来指定capacity的初始化大小

1.初始化大小为15

在这里插入图片描述

2.调用有参构造器,通过源码就可以看出在第153行,就直接把数组的长度设置为我们传进去的值。接着后面扩容的步骤也是oldCapacity的1.5倍
在这里插入图片描述

1.4.3有参构造和无参构造总结

  • 如果是有参构造器,初始大小为传入的容量。往后的每次扩容都为原来的1.5倍
  • 如果是无参构造器,第一次扩容大小为10。往后的每次扩容都为原来的1.5倍

猜你喜欢

转载自blog.csdn.net/weixin_48244108/article/details/123502747