问题:ArrayList数组是怎么扩容,扩容的特点,能扩容到多少
源码分析
- ArrayList默认容量为10,没有加载因子,一次最大扩容为16。
- 无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示的是空的数组对象。
- 有参构造:
第一种:传int参数
如果这个初始化参数大于0,那么就会设置对象大的数组存储区的容量大小与初始化参数相等。
如果初始化参数等于0,那就返回EMPTY_ELEMENTDATA的空数组。
第二种:传Collection
首先,将Collection转化成数组,赋值给elementData,如果size不为0,那么就判断elementData是不是属于Object数组(这是为了避免出现在集合转数组时可能存在不返回Object数组的情况)。
如果size为0,那就返回EMPTY_ELEMENTDATA的空数组。
小结:
- EMPTY_ELEMENTDATA 表示的是我们在指定size为0的时候的空数组。
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示的一个默认的数组,一个空数组,没有特别指定它的size为0,它处于一个默认的、空的状态。
扩容机制:
- ArrayList扩容发生在add()方法调用的时候, 调用ensureCapacityInternal()来扩容的,通过方法calculateCapacity(elementData, minCapacity)获取需要扩容的长度。
- ensureExplicitCapacity方法可以判断是否需要扩容:
2.1 首先进行判断是否大于默认容量10
2.2 如果,小于默认容量10,直接在原来基础上+1,元素添加完毕
2.3 如果,大于默认容量10,则需要进行扩容,扩容核心是 grow()方法 - ArrayList扩容的关键方法grow():
获取到ArrayList中elementData数组的内存空间长度 扩容至原来的1.5倍
3.1 扩容之前,首先创建一个新的数组,且旧数组被复制到新的数组中,这样就得到了一个全新的副本,我们在操作时就不会影响原来数组了。
3.2 然后通过位运算符,将新的容量更新为旧容量的1.5倍。
3.3 如果新的容量-minCapacity<=0,就拿新的容量-最大容量长度如果<=0的,那么最终容量=最大容量长度。
3.4 如果新的容量-minCapacity>0,那么最终容量=新的容量。 - 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间。
从此方法中我们可以清晰的看出其实ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。
(例如:添加的容量为15,则minCapacity=15,15-16<=0,那么最终容量就是16。)
(例如:添加的容量为11,则minCapacity=11,15-11>0,那么最终容量就是15。)
特点:
- ArrayList有自动扩容机制,它至少会保障这个数组的最小容量和你的数组大小一致,但是ArrayList的扩容机制并不是完全确定的,而且每次添加一个元素会消耗特定不变的缓冲时间。