先看一下集合的继承结构图
1.ArrayList中维护了一个Object类型的数组elementData。
2.当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
3.如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容则直接扩容elementData为1.5倍。
ArrayList的特点包括:
- 动态大小:ArrayList的大小可以根据需要自动调整。它会根据添加或删除元素自动增加或减小容量,无需手动调整数组大小。
- 随机访问:ArrayList通过索引来访问元素,这意味着可以在O(1)的时间复杂度内访问特定位置的元素。因为ArrayList底层基于数组实现,所以可以直接通过下标访问元素。
- 可以存储任意类型的对象:ArrayList是泛型类,它可以存储任何类型的对象,包括基本数据类型的包装类和自定义对象。
- 有序集合:ArrayList中的元素按照添加的顺序排列,可以维持元素的插入顺序。
- 支持重复元素:ArrayList允许存储重复的元素,同一个元素可以多次出现在列表中。
- 方便的操作方法:ArrayList提供了一系列方法来操作和管理元素,包括添加、删除、修改、获取元素等。这些方法使得对元素的操作更加方便和灵活。
- 很少使用的位置为空:当删除元素时,ArrayList会将被删除元素的位置设置为空,但不会立即移动其他元素来填充空缺,而是在后续添加元素时才进行填充。
源码分析:
我们使用的无参构造器,所以应该像我们说的一样,初始elementData容量为0,如图所示:
首次添加数据时,调用add(E e)方法 e是传入的数据
跳入add(E e, Object[] elementData, int s)方法
在这里 s传入的是size值,初始为0,因此if条件成立,执行grow方法
进入此grow()方法,此grow方法返回一个Object数组
再次进入这个grow(int minCapacity)方法
首先把elementData数组的长度赋值给oldCapacity,然后进行判断。因为初始的elementData被这个常量赋值为0,并且oldCapacity等于0,所以if判断不成立。
通过max()方法,为elementData赋新值。
可以看到 DEFAULT_CAPACITY的值为10。
执行完这个方法后,返回一个长度为10的elementData数组
这样,ArrayList完成了它的第一次扩容。
接下来,我们来看当存入了十个数据后,如果要存入第十一个数据,ArraryList如何再次扩容。
首先还是一样,执行add(E e)方法,此时size已经等于10了。
然后执行add(E e, Object[] elementData, int s)方法,由于现在的数组长度等于了第一次扩容后的数组长度,即s = elementData.length ,所以判断成立,执行grow()方法。
还是和之前一样,再执行grow(int minCapacity)方法
此时oldCapacity = 10,已经大于0了,判断条件成立,执行if方法中的内容
这段代码设计数组扩容过程,具体解释如下:
ArraysSupport.newLength是一个用于计算新数组长度的方法,这个方法的目的是根据现有的数组容量、最小需要的容量以及偏好的容量增长值,计算出新数组的合适长度。
oldCapacity代表当前数组的容量大小。
minCapacity - oldCapacity的含义是最小需要容量与当前容量的差值,表示数组需要增长的最小空间大小。
oldCapacity >> 1是对当前容量进行右移一位操作,相当于将当前容量除以2,用作数组扩容时的偏好增长值。
newCapacity是根据上述参数计算得出的新数组容量。
这段代码执行后,把newCapacity赋给elementData,完成1.5倍扩容。
然后是返回elementData,可以看到执行grow()方法后,elementData的长度变成了15。
以上就是ArraryList数组扩容机制的源码分析。
前路漫漫亦灿灿。