ArrayList源码分析–底层扩容机制
最近在复习Java基础知识,故写此博客记录
先说结论:如果我们在初始集合时使用无参构造形式,那么ArrayList的初始大小为0,当我们第一次添加数据时,数组大小为10,后续每次添加数据时(我们规定每次只添加一个数据,原因我们后面会说到)如果容量不够,则集合扩容为原来的1.5倍数 Plus:此程序运行的环境为jdk15,不过8版本机制和这个大体上差不多
以下为演示过程
首先我们写这样一个简单的程序:
我们开始调试程序。
首先,我们先按 Ctrl+A进入查看AyyayList的空构造方法
看不懂没关系,我们往上翻来找这个DEFAULTCAPACITY_EMPTY_ELEMENTDATA
从这里我们就可以发现,其实这段话的含义就是把ArrayList初始化为一个空数组,让然后我们跳出来继续看。
此时集合的size(大小)为0,也印证了我们的想法
下面我们来看add方法
首先时对int数据类型的装箱,变成integer类型,然后我们跳出来继续看
这里的modCount记录着我们的操作次数我们先不管它,方法里面又含有一个add方法,e就是我们要添加的元素,elementData是我们要操作的数组,size是现在集合的大小,我们进入这个add方法
首先是检验数组的容量是否足够用于扩容,即检验下标是否等于数组容量,如果为true,说明容量已经不足了,false则表示还有容量也就自然不需要执行扩容操作了,这里为true,我们执行扩容操作
这个时候我们才开始执行扩容操作grow方法
这里的minCapacity是现在集合最小需要的容量
oldCapacity:数组现在的容量,
然后判断集合大小是否大于0或者不为空,否,我们进入这个看起来很长的方法
这里操作其实很显而易见,还记得我们之前的结论吗?第一次扩容(这里我们讨论的是无参构造的情况),扩容后的大小为10。
这里做的就是这一步操作,如果我们需要的容量(此时是1)小于10,那么我们就新创建一个大小为10的数组,然后赋值给elementData也就是我们要操作的数组,如果需要的容量大于10,比如为x(x>10),那么就创建一个大小为x的数组,然后将它赋值给elementData,OK结束。
至此我们一直出栈,发现elementData的大小确实变成了10,将值e放入数组集合的第一个位置,然后size+1。
至此我们的第一次扩容工作就结束啦。是不是觉得很简单呢。
后续的9次循环,就是正常添加数据,并不会触发扩容操作,原因上面我们已经说过了,判断语句是这句话。
好,接下来9次循环我们就跳过,我们来看下一次容量不够时程序执行的操作。
此时集合容量还是10,但我们即将插入第11个数据
前面一堆跟上面一样的操作我就不再讲了,直接开讲不一样的。
首先是程序检测到集合已满不足以添加新数据,即将执行grow方法
此时我们的集合已经大于0了,开始执行不一样扩容的操作
我们首先进入ArraysSupport的newLength方法,这里有三个参数
oldLength,minGrowth,prefGrowth
oldLength时原来集合的大小,minGrowth是集合最小需要扩容的大小,值为:需要添加的数据数+集合的原来大小,prefGrowth是集合现在大小的一半,考虑到有些朋友可能不明白>>符号的意思,这里我找了一篇文章大家可以参考看一下Java中的>>,>>>
好啦我们继续
又进入了一个方法
第一句话(关键点):int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
找出我们需要扩容的最大大小,这也就是为什么我们在开头的结论中规定每次只添加一个数据,如果一次性添加的数据数x大于集合大小的一半y/2,那么我们扩容后的大小就不是原来的1.5倍(y/2+y)了 而是x+y。
第二句话是检验集合容量是否很大,这里我们不做讨论。
本次的结果就是返回一个值,该值为现在集合容量的1.5倍。
然后我们使用Arrays的copyOf方法来实现集合的扩容,这里我提一下为什么要用copyOf方法,因为这样可以保证原来的数据还存在,如果创建一个新的对象的话,原来的数据就不存在了。后续就是一些赋值什么的操作啦。
以上就是我对于数组集合的一些理解,本人学艺不精如果有讲错的地方请指正!