ArrayList扩容源码详细解读(JDK1.8)

Posted efggfxfvhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList扩容源码详细解读(JDK1.8)相关的知识,希望对你有一定的参考价值。

前言

大部分文章针对于ArrayList扩容是这样解释的:当所需长度即当前ArrayList元素数量加一大于当前ArrayList长度时,触发扩容,扩容长度为ArrayList长度加ArrayList长度右移一位。
这不经让我产生一个疑问,扩容会无限制扩容下去吗?带着这个疑问,我仔细阅读了Arraylist扩容源码,发现源码对于ArrayList扩容处理是非常详细严密的。

一、源码是如何处理Arraylist扩容的

1.当ArrayList现长度小于整型最大值减八时,理论扩容长度等于现长度加现长度右移一位。
(1)当理论扩容长度小于整型最大值减八时,最终扩容长度等于理论扩容长度。
(2)当理论扩容长度介于整型最大值和整型最大值减八时。最终扩容长度等于整形最大值减8。
(3)当理论扩容长度大于整型最大值时,最终扩容长度等于现长度加1。
2.当Arraylist现长度等于整形最大值减8时,最终扩容值等于整型最大值。
3.当Arraylist现长度等于整形最大值时,再添加元素,抛出异常。

二、源码解读

1.新建ArrayList对象,调用add方法

2.点击进入add方法


可以看到最先进入的是ensureCapacityInternal方法,形参size是Arraylist的成员变量,实例化Arraylist对象时默认为0,因而第一次调用add时,ensureCapacityInternal方法的入参是1。

3.进入ensureCapacityInternal方法


首先进入的是if判断语句,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是静态最终常量,值是空数组。elementData是数组型成员变量,实例化ArrayList对象是默认为空数组。固首次调用add方法时,会进入if条件判断语句。
if判断语句中DEFAULT_CAPACITY为静态最终常量,值是10。这里可以看出,ArrayList在调用add方法时被初始化,赋予默认长度为10。

4.进入方法 ensureExplicitCapacity


ArrayList是线程不安全的,modCount的作用是记录修改次数,当遍历该数组时,其它线程修改该ArrayList时能抛出异常。
if中的判断语句minCapacity=步骤一的size+1,即当前ArrayList元素加1大于ArrayList长度时,触发扩容。

5.进入最关键的 grow(minCapacity);


ArrayList扩容最核心的代码就在这里。本文大标题一即源码是如何处理ArrayList扩容就是根据该方法得出。
第一个条件判断中newCapacity等于ArrayList当前长度加Arraylist当前长度右移一位。minCapacity表示ArrayList当前长度加1。理论上newCapacity是不会小于minCapacity。但是计算机中有整型最大值的存在,当超过整形最大值时,二进制最前面的位数即符号位会变成负号,也就是说,当newCapacity超过整型最大值时,newCapacity会变成负数,故而会小于minCapacity。小于则将minCapacity赋值给newCapacity。(理论扩容值超过整型最大值时,扩容长度为当前长度加一)
第二个判断语句指当newCapacity大于整型最大值-8,进入hugeCapacity方法。

进入hugeCapacity中的方法,第一句if的触发条件是当当前ArrayList等于整型最大值时,这时再加元素,会抛出异常。return中绝大多数情况minCapacitcy都是大于Max_ARRAY_SIZE(整型最大值减8),只有很偶然的情况,即newCapacity等于ArrayList当前长度加Arraylist当前长度右移一位正好落在整型和整型最大值减8之间,才会触发Max_ARRAY_SIZE赋值给minCapacity。

总结

经过源码解读后,重新理解一下源码是如何处理ArrayList扩容的
1.当ArrayList现长度小于整型最大值减八时,理论扩容长度等于现长度加现长度右移一位。
(1)当理论扩容长度小于整型最大值减八时,最终扩容长度等于理论扩容长度。
(2)当理论扩容长度介于整型最大值和整型最大值减八时。最终扩容长度等于整形最大值减8。
(3)当理论扩容长度大于整型最大值时,最终扩容长度等于现长度加1。
2.当Arraylist现长度等于整形最大值减8时,最终扩容值等于整型最大值。
3.当Arraylist现长度等于整形最大值时,再添加元素,抛出异常。

精简得概括一下,ArrayList大多数情况下都是在原基础长度加原基础长度右移一位。某一时刻,这个长度超过整形最大值了,这时候只能一位一位加。当加到整形最大值减八时,下次扩容至整形最大值。因为有些虚拟机会存储对象头,占八位(这里我的理解是java无法判断哪些虚拟机存了表头,所以事先预留了八位,但如果不够用,只能冒着可能抛出异常的风险,将最后八位也放出来,但如果还不够,继续加元素,变会抛出异常。)。

以上是关于ArrayList扩容源码详细解读(JDK1.8)的主要内容,如果未能解决你的问题,请参考以下文章

ArrayList源码解读(jdk1.8)

JDK1.8源码学习-ArrayList

JDK1.8源码之ArrayList

jdk1.8ArrayList主要方法和扩容机制(源码解析)

ArrayList源码分析--jdk1.8

JDK1.8中ArrayList的实现原理及源码分析