Java基础知识中面试必问的包括集合,我们不仅要掌握集合语法的使用,也要了解集合体系中每个分支的底层数据结构。掌握了这些对集合已经有了比较深入的认识,接下来就是针对具体的细节深究,本文旨在通过追踪源码带大家初次认识一下ArrayList的扩容原理
提到ArrayList,我们可以脱口而出底层数据结构是数组,但是如果被追问,刚开始的时候ArrayList存数据时候,数组容量是多少?如果存的数据超过了初始容量的大小,该怎么?-----数组扩容增大容量。 扩容的细节是什么?带着这些疑问我们往下步步探索!
创建ArrayList有两种方式,使用无参构造器和使用有参构造器,两个办法的扩容逻辑也有区别
先看结论:
无参构造器:刚开始数组容量大小为0,第一次扩容到10,第二次扩容到1.5倍-----15,第三次扩容到1.5倍------22,第四次扩容到1.5倍-----33…
有参构造器:刚开始数组容量为自定义的大小,后面为1.5倍
无参构造器创建ArrayList的演示代码(在a1处打断点调试)
@Test
public void Test02(){
List list = new ArrayList();
//elementDate初始容量为0
//第一次扩容到10
list.add("a1"); //断点
list.add("a2");
list.add("a3");
list.add("a4");
list.add("a5");
list.add("a6");
list.add("a7");
list.add("a8");
list.add("a9");
list.add("a10");
//需要扩容到15
list.add("a11"); //断点
list.add("a12");
list.add("a13");
list.add("a14");
list.add("a15");
//第三次扩容到22
list.add("a12"); //断点
}
现追踪第一次扩容底层执行流程,看看如何给数组分配大小并且存数据 a1
接下来该第二次扩容了,我们先不看源码,先根据上面的逻辑自己分析一下怎么实现的。
- 进入add(E e)方法{ 调用执行方法ensureCapacityInternal(参数为 10+1)}
- 调用执行方法ensureExclicitCapacity( CaculateCapacity(10,11))
- 调用执行方法CaculateCapacity(10,11){
if(11== 0)不成立 所以直接 return 11} - 调用执行方法ensureCapacityInternal(参数为 11)
- 调用执行方法ensureExclicitCapacity( CaculateCapacity(10,11))
- 调用执行方法ensureExclicitCapacity(参数为11){
if(11- 10 >0)成立--------->说明需要11个,现在只提供10个,需要扩容} - 调用执行方法grow(11){
oldCapacity =10
newCapacity = 10 + 10/2 = 15
if(15-11 < 0 ) 不成立-------->说明 提供的15个空间 足够 所需要的最少11个 的条件
执行扩容方法Arrays.copyOf(10,15) - 总共15个空间已经扩容结束,elementData[10]存入元素 a11
经过debug调试验证,完全符合我们的推测。
拓展:解释一下图示方法