ArrayList 是日常开发经常使用到的容器类。它能够方便的进行数据的查询、替换。但是因为其低层实现的原因在数据容量、性能、线程安全上都存在问题,主要涉及到下面的内容:

(1)默认初始容量为 0,如果未指定容量则首次初始的容量为 10;同时其也是有容量限制的;

(2)添加元素会涉及到数组扩容和数组元素拷贝,删除数组元素时同样也会涉及到数组的拷贝,这都会影响性能;

(3)线程不安全,因为整个ArrayList 中没有涉及到线程安全的相关代码;

下面是关键信息的分析。

01,ArrayList 的主要的属性

打开ArrayList 类之后,主要涉及到以下几个属性:

很显然 ArrayList 内部是通过数据来实现的,而数据就存储在 elementData 数组中,且允许存储的最大元素数是 MAX_ARRAY_SIZE。

EMPTY_ELEMENTDATADEFAULTCAPACITY_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 中提供了 fasetRemove() 方法,其实现如下:

可以看到 AarrayList 有多处会用到 System.arrayCopy() 方法。

因此在使用ArrayList时,如果考虑到性能

(1)初始化时指定初始化的容量大小,避免 add() 操作时的数据扩容和数组拷贝;

(2)避免数组中数据的删除操作;

ArrayList 中还提供了很多实用方法,如果感兴趣可以详细阅读其实现。

晓锋 技术交流

发表评论

电子邮件地址不会被公开。 必填项已用*标注