一、ArrayList的实现原理?
1、ArrayList概述?
ArrayList 是 List 接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。
注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。
2. ArrayList 的实现
对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。下面我们来分析ArrayList的源代码:
1) 底层使用数组实现
Java 代码
2)构造方法:
ArrayList 提供了三种方式的构造器,可以构造一个默认初始容量为 10 的空列表、构造一个指定初始容量的空列表以及构造一个包含指定collection的元素的列表,这些元素按照该collection的迭代器返回它们的顺序排列的。
Java 代码
a:构造一个默认初始容量为 10 的空列表的构造器:
b:构造一个指定初始容量的空列表的构造器
c:构造一个包含指定collection的元素的列表的构造器
注:详细可见ArrayList的源码。
3)存储
ArrayList 提供了set(int index, E element)、add(E e)、add(int index, E element)、addAll(Collection c)、addAll(int index, Collection c)这些添加元素的方法。
下面我们一一讲解:
Java 代码
以上均是ArrayList的源码,有兴趣的可以看一下,有助于理解。
4)读取
java 代码
5)删除
ArrayList 提供了根据下标或者指定对象两种方式的删除功能。如下:
java 代码
第一种:
第二种:
注意:从数组中移除元素的操作,也会导致被移除的元素以后的所有元素的向左移动一个位置。
6)调整数组容量:
从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。
从上述代码中可以看出,数组进行扩充时,会将数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其容量的1.5倍。这种操作的代价是很高的,因此在实际使用中,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少是,要在构造ArrayList实例时,就指定其容量,以避免扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。
ArrayList还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize方法来实现。代码如下:
trimToSize
7)Fail-Fast机制
ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着将来某个不确定时间发生任意不确定行为的风险。