Java 集合系列(三)ArrayList 实现原理

ArrayList 实现原理

1. ArrayList 概述

ArrayList 是 List 接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。

每个ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList 时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity 操作来增加 ArrayList实例的容量,这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

2. ArrayList 的实现

对于 ArrayList 而言,它实现 List 接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析ArrayList 的源代码:

1) 底层使用数组实现

      private transient Object[] elementData;

2) 构造方法

ArrayList 提供了三种方式的构造器,可以构造一个默认初始容量为 10 的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection 的元素的列表,这些元素按照该collection 的迭代器返回它们的顺序排列的。

3) 存储:

ArrayList 提供了 set(int index, E element)、 add(E e)、 add(int index, E element)、

addAll(Collection<? extends E> c)、 addAll(int index, Collection<? extends E> c)这些添加

元素的方法。

下面我们一一讲解:

4) 读取

5) 删除

ArrayList 提供了根据下标或者指定对象两种方式的删除功能。如下:

注意:从数组中移除元素的操作,也会导致被移除的元素以后的所有元素的向左移动一个位置。

6) 调整数组容量

从上面介绍的向 ArrayList 中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity 来手动增加 ArrayList 实例的容量,以减少递增式再分配的数量。

从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5 倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造 ArrayList 实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity 方法来手动增加 ArrayList 实例的容量。ArrayList 还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize 方法来实现。

代码如下:

7) Fail-Fast 机制

ArrayList 也采用了快速失败的机制,通过记录 modCount 参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。具体介绍请参考我之前的文章深入Java 集合学习系列: HashMap 的实现原理 中的Fail-Fast 机制。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。