优点:有序,访问元素速度快.
缺点:插入,删除速度慢。
JDK1.8.131版本。
首先查看ArrayList实例化方法相关代码。
默认无参构造方法。elementData实际存放数据元素,从这些可以看出,ArrayList底层是用数组来实现,元素是连续的;这也体现了ArrayList有序,读取快,当然数组维护角标,如果在ArrayList元素里面随机删除个元素,就比较慢了。因为需要维护角标,所以被删除元素后面元素的角标都需要-1。
我们知道ArrayList底层是使用数组来实现的,但是数组是不可变;ArrayList是怎么实现,动态改变数据元素的呢?这个通过示例(查看源码)来说明问题以及原因。
使用ArrayList的无参构造方法,初始化list集合,并使用add方法添加一个"a"元素。
首先,ArrayList先将内部的 elementData 初始化(elementData = {}),这个时候数组的长度是0,list集合的长度也是0.
然后,add方法添加元素,方法中先执行ensureCapacityInternal(size + 1)方法。
add方法以及其涉及到的方法:
在ArrayList将元素添加到集合中之前,先执行了ensureCapacityInternal方法,执行这个方法的目的是检查集合中元素(数组)是不是还能再插入一个元素,不能则扩容。
流程如下:
先检查elementData数组是否是默认初始化的数组,如果是则选择默认初始化容量和最小容量数值大的那一个,再将数值赋值给minCapacity变量;如果elementData.lengh小于minCapacity,则扩容(grow).每次扩容后的容量为int newCapacity = oldCapacity + (oldCapacity >> 1);(之前容量的1.5倍)。如果newCapacity(扩容后的容量)还小于(minCapacity),则直接将新容量定义为minCapacity;如果newCapacity大于了MAX_ARRAY_SIZE,则比较minCapacity和MAX_ARRAY_SIZE的大小,大于则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE,到此扩容后的数组长度确定了,接着使用Arrays.copyOf方法,将之前元素,放入新的数组中,最后执行add方法中elementData[size++] = e,将元素放到集合中。至此添加元素流程完成。
自顶一个ZArrayList,来模拟ArrayList。
首先定义全局变量int count = 0,在扩容方法grow中,使用count+=1,这样能大致得出扩容次数。测试代码
结果如下:
从这些数据可以得出如果依次添加100个元素,大概会扩容7次,如果将数据依次删除,但是list内部数组的长度不变。
特别注意:ArrayList内部数组Object[] elementData的长度不等于ArrayList的size,通过源码可以看到在add/addAll/remove/removeAll中都维护了一个size变量,这个才是ArrayList元素的个数。
从上边的分析中可以得出,如果添加数据量比较大的话,最好是给ArrayList一个默认的初始容量,用以减少扩容的次数。
以此类推ArrayList的其他方法addAll和remove方法,原理是相似的,不再赘述。
本文转自 墨宇hz 51CTO博客,原文链接:http://blog.51cto.com/zzhhz/2048333