最近在看《算法与数据结构Java版》这本书,正好看到第三章线性表,就顺便看了一下ArrayList和LinkedList的源码,深入的了解了他们之间的区别和性能方面的优缺点。先单独说一下ArrayList的实现原理。
概要
ArrayList的实现原理其实就是数组,它是线程不安全的,允许其中元素为null。
它实现了 List<E>, RandomAccess, Cloneable, java.io.Serializable
接口,
其中RandomAccess
代表了其拥有随机快速访问的能力,ArrayList
可以以O(1)的时间复杂度去根据下标访问元素。
因为其底层结构是数组,所以它是占据了一块连续的内存空间,其长度就是数组的大小,因此它有数组的缺点,空间效率不高,但是也有优点,就是查询速度快,时间效率很高。
当集合中的元素超出数组的长度是,数组就会进行扩容操作,扩容操作是ArrayList
存储操作缓慢的主要原因,尤其是当数据量越来越大,每次扩容消耗的时间会越来越多。
所以如果我们事先预知数据量的大小,可以通过public ArrayList(int initialCapacity) {}
构造方法来指定集合的大小,以减少扩容次数,提高写入效率。
构造方法详解
//集合的初始容量为10
private static final int DEFAULT_CAPACITY = 10;
//默认构造函数里的空数组
private static final Object[] EMPTY_ELEMENTDATA = {
};
//真正存放元素的数组
private transient Object[] elementData;
//自定义初始容量的构造方法
public ArrayList(int initialCapacity) {
super();
//如果初始容量小于0则报IllegalArgumentException异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
this.elementData = new Object[initialCapacity];
}
//默认的构造方法
public ArrayList() {
super();
//默认的构造方法是将空数组赋值给了elementData
this.elementData = EMPTY_ELEMENTDATA;
}
//将别的集合直接赋值进行创建的构造方法
public ArrayList(Collection<? extends E> c) {
//直接用toArray()的方法获取集合的数组对象,并且直接赋值给elementData
elementData = c.toArray();
size = elementData.length;
// 这里是当c.toArray出错,没有返回Object[]时,利用Arrays.copyOf 来复制集合c中的元素到elementData数组中
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
调用构造方法后,会构造出数组elementData和集合大小size。
常用API详解
1 添加元素
每次添加元素之前都会判断添加后的容量是否需要扩容。
//添加单个元素
public boolean add(E e) {
//判断添加后的长度是否需要扩容
ensureCapacityInternal(size + 1);
//在数组末尾添加上当前元素,并且修改size大小
elementData[size++] = e;
return true;
}
//集合的初始容量为10
private static final int DEFAULT_CAPACITY = 10;
//判断是否是第一次初始化数组
private void ensureCapacityInternal(int minCapacity) {
//判断当前数组是否 == EMPTY_ELEMENTDATA,因为默认构造函数创建时是将空数组EMPTY_ELEMENTDATA赋值给elementData
if (elementData == EMPTY_ELEMENTDATA) {
//判断默认容量10和当前数据长度的大小,取其中大的值作为判断本次是否需要扩容的依据,由于第一次数组是空的,所以默认要使数组扩容到10的长度
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//判断是否需要扩容
ensureExplicitCapacity(minCapacity);
}
//判断扩容的方法
private void ensureExplicitCapacity(int minCapacity) {
//如果需要扩容modCount++,此参数是指当前列表结构被修改的次数
modCount++;
// 判断当前数据量是否大于数组的长度
if (minCapacity - elementData.length > 0)
//如果大于则进行扩容操作
grow(minCapacity);
}
//扩容方法
private void grow(int minCapacity) {
// 记录扩容前数组的长度