咱们主要分析一下,实例化的过程和数组扩容的过程。
为什么要看这个呢,这几天面试的时候,发现很多面试的人并不知道初始化数组长度是多少,每次扩容是多少,所以我也抱着怀疑的态度,自己看一下ArrayList的源码。
首先我看看一下自己的JDK的版本信息
在提前准备好例子,可以更好的看源码。
1.实例化的过程
直接进去ArrayList()的方法里面,可以看到下面的这个图。
这里我们就发现我们如果使用默认的实例化方式的话,其实数组里面是个空数组的。
还有一个是带Integer的初始化方式,源码看下面。
这个看起来就比较简单了,如果实例化的initialCapacity值大于0,那么初始化数组长度就是initialCapacity。如果initialCapacity是空,声明出来的还是一个空数组。如果小于0,就直接抛出异常就可以了。
当然了,初始化的方式不至这两种,还有参数是Collection的方式,这种大家自己看看就好了,很好理解的。
2.ArrayList是怎么添加数据的
直接进入add方法,看看里面是怎么写的就好了。
第一次看到这个,我就很懵逼,我说这个是个什么东西呢?
elementData[size++] = e;这个肯定是个复制操作的。
那么ensureCapacityInternal(size + 1);这个里面就是有扩容的操作的。
进入到ensureCapacityInternal的方法里面看看是怎么个扩容的。
都是一些看不懂的英文,自己也不知道这个里面的两个方法是做了什么,然后先去calculateCapacity里面看看他的逻辑是什么
这个里面的minCapacity就是size+1,也就是数组长度+1,默认情况下size+1就是1,calculateCapacity返回值就是10了。
继续跟下一个函数ensureExplicitCapacity,参数是个Integer,我们再来看看这个函数里面做了什么操作吧。
modCount是什么呢?按字面里面就是修改的次数,是ArrayList的一个成员变量。
在下面就是判断一个minCapacity和数组长度大小谁大,如果minCapacity大,就执行grow函数,这个行数也就是扩容的核心函数了。如果数组大,就不做任何操作。
下面继续看看grow里面,都做了什么吧,直接上图,看源码。
int newCapacity = oldCapacity + (oldCapacity >> 1);直接就找到了扩容多大的方法,这才知道,扩容的时候是进行了位操作的。
虽然知道了每次是怎么扩容的,但是并不知道每次都是这么扩容的,看一下下面的两个if判断的逻辑又是什么呢?
如果newCapacity比minCapacity小,就newCapacity就用minCapacity,正常情况下,如果数组长度大于2的情况,一般都是大于minCapacity的。
就下来就是比较newCapacity和MAX_ARRAY_SIZE的大小了。
我们看一下MAX_ARRAY_SIZE是Integer.MAX_VALUE - 8,如果newCapacity比MAX_ARRAY_SIZE大,那么就需要看一下hugeCapacity里面的方法了。
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
这段就很好理解了,如果超过MAX_ARRAY_SIZE就是Integer.MAX_VALUE,如果没有最后就是MAX_ARRAY_SIZE。
所以这里扩容的时候最大的数组大小就是Integer.MAX_VALUE,也就是2 的 31 次方 - 1 = 2147483648 - 1 = 2147483647。
3.疑问
ArrayList有没有缩容的情况呢?这个就大家找一下源码就知道了,我也找了一下remove的源码,这个大家也可以动手去看一下。
clear的时候,数组大小会不会表呢?