ArrayList 是日常开发经常使用到的容器类。它能够方便的进行数据的查询、替换。但是因为其低层实现的原因在数据容量、性能、线程安全上都存在问题,主要涉及到下面的内容:
(1)默认初始容量为 0,如果未指定容量则首次初始的容量为 10;同时其也是有容量限制的;
(2)添加元素会涉及到数组扩容和数组元素拷贝,删除数组元素时同样也会涉及到数组的拷贝,这都会影响性能;
(3)线程不安全,因为整个ArrayList 中没有涉及到线程安全的相关代码;
下面是关键信息的分析。
01,ArrayList 的主要的属性
打开ArrayList 类之后,主要涉及到以下几个属性:
很显然 ArrayList 内部是通过数据来实现的,而数据就存储在 elementData 数组中,且允许存储的最大元素数是 MAX_ARRAY_SIZE。
EMPTY_ELEMENTDATA
和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
主要用在实例化 ArrayList 时。
当使用无参构造函数 new ArrayList()
时默认数组大小默认是大小为 0 的数组。
当使用有参构造函数 new ArrayList(initCapacity)
时,如果 initCapacity 小于等于 0 ,则默认数组大小为 0 的数组。
02,当执行 add() 方法时会发生什么?
如下图所示,add 过程主要涉及到两步操作:ArrayList 中数组扩容;赋值;
数组扩容的过程如下:
(1)计算新的容量;
(2)将数组元素进行拷贝到扩容后的数组;
如下面的代码所示:
上面扩容时有两步逻辑,
如果存储数据的数组大小为 0,则新创建的数组大小时为 10;
如果新容量没有超过最大容量,则使用新容量。新容量的计算 = 原来的容量 + 原来容量的一半,例如:
150 = 100 + 100 >> 1
03,当执行 remove() 时会进行数组拷贝
在 ArrayList 中提供了 fastRemove() 方法,其实现如下:
可以看到 ArrayList 有多处会用到 System.arrayCopy() 方法。
因此在使用ArrayList时,如果考虑到性能
(1)初始化时指定初始化的容量大小,避免 add() 操作时的数据扩容和数组拷贝;
(2)避免数组中数据的删除操作;