ArrayList
日常开发过程中List
接口的使用非常频繁,有序且元素可重复,有三个实现类;分别是 ArrayList
、LinkedList
、Vector
。
比较常用的还是前两者,ArrayList
的特点是随机访问数据速度比较快。时间复杂度为 O(1)
,但是当数据量很大的时,ArrayList
的增删速度明显要低于 LinkedList
的,时间复杂度为 O(n)
。主要的继承关系为:
ArrayList
底层的实现,总结来说就是用到了可扩容的数组结构,并且实现了标记接口RandomAccess
(这个接口没有具体的实现,ArrayList
可以被随机访问任意下标的元素,实现RandomAccess
接口,有点打上tag
的意思,某些后续操作会根据这个tag
作出不同的处理)。
ArrayList的结构与常量
源码中(JDK1.8
)实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class ArrayList<E> extends AbstractList<E>
//继承关系与实现的接口
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//Default initial capacity 默认的数组长度即容量
private static final int DEFAULT_CAPACITY = 10;
//Shared empty array instance used for empty instances 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认大小共享数组实例,与EMPTY_ELEMENTDATA区分
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//元素容器
transient Object[] elementData;
//数组的长度及实际元素的个数
private int size; ...}
从几个常量可以看出,ArrayList底层本质上就是扩容数组,默认的容量的为10,并且区分了容量为0的共享数组,扩容需要区别对待。
构造函数
1 | //有参构造函数,可以指定底层的数组容量 |
默认的构造函数:1
2
3
4
5//Constructs an empty list with an initial capacity of ten.
//构造一个初始容量为10空列表(默认的容量为10)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
扩容机制
1 | public void ensureCapacity(int minCapacity) { |
底层定义的数组最大值:1
2
3
4
5
6
7/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
对于为什么定义为 Integer.MAX_VALUE - 8; 而不是直接为 Integer.MAX_VALUE; 注释里给出了解释,
stackoverflow 上也作了解释。
总结:ArrayList
在增加元素的时候,会检查容量是否满足,对于DEFAULTCAPACITY_EMPTY_ELEMENTDATA
扩容后变为原来容量的1.5倍。最大能取值为Integer.MAX_VALUE
。例如minCapacity
大小为Integer.MAX_VALUE - 7
比MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
大,根据代码:
1 | minCapacity = Integer.MAX_VALUE - 7; |
增加元素
1 | //Appends the specified element to the end of this list. |
修改元素
1 | public E set(int index, E element) { |
查找元素
1 | //常用方法,根据索引获得对应的值 O(1) |
思考
ArrayList
的底层实现还是很容易理解的,显然ArrayList
的底层使用的是数组结构,随机访问的时间复杂度为O(1),但是通过源码可以看出,如果元素量过大,频繁的增删,性能上是吃不消的(增删,主要考虑到扩容,拷贝数据,时间复杂度为O(n))。平时开发过程中应该根据情况而定,要求随机访问但不会频繁的增删,性能上影响不会很大。区别于链表结构,增删快,查找慢的特点。