ArrayList源码详解
一,ArrayList字段
/**
* 默认初始化的容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 实例共享的空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
默认大小的实例共享的空数组,与上面的EMPTY_ELEMENTDATA区别是在第一个元素被添加时可以知道数组扩大了多少
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
存储数组列表元素的数组缓冲区。数组列表的容量是这个数组缓冲区的长度。当添加第一个元素时,带有元素Data == DEFAULTCAPACITY empty ELEMENTDATA的空数组将会扩展到默认容量。
*/
transient Object[] elementData;
/**
* 数组列表的逻辑大小(它包含的元素的数目)
*/
private int size;
二,ArrayList的构造方法
第一个构造方法:
构造一个初始容量为 10 的空列表
至于为什么为10 这个在后面add方法里再讲。
第二个构造方法:
第二个构造方法支持传入一个int类型的参数,用作初始化数组大小
第三个构造方法:
第三个构造方法有点复杂,传入参数为Collection c。
第一步调用c.toArray()方法。toArray方法存在collection接口中,其返回
值是一个Object数组。
public interface Collection<E> extends Iterable<E> {
//将包含该集合中的所有元素的数组e转换
Object[] toArray();
所以其子类肯定有自己的具体实现。我们只需要调用他就好。
调用完toArray方法之后会初始化ArrayList。初始化的值为toArray的返
回值。
接下来会判断传入的参数c转换成数组之后是不是为空数组。如果是空数
组,则初始化ArrayList,初始值为空数组。
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
如果不为空,则判断elementData是不是Object类型的数组,因为有时候toArray有可能会错误的返回一个不是Object类型的数组,所以这个时候要判断一下。如果是,则构造方法结束。如果不是 ,则调用Array.copyof方法将旧数组转换为Object类型。
ArrayList的增删改查
增
public boolean add(E e) {}
在代码elementData[size++] = e;
之前调用了ensureCapacityInternal(size + 1);
这个方法,该方法确保了数组空间的容量是不是够用,以及数组空间的扩容。
ensureCapacityInternal(minCapacity);方法
这个方法其实就是判断是不是用了空构造器new的对象,如果是,那么在这个方法 里初始化了一个大小为10 的数组。
接着调用方法:
ensureExplicitCapacity(minCapacity);
该方法确保了当数组元素数量超过了数组容量时 数组扩容的问题。
ensureExplicitCapacity(minCapacity);方法
如果数组元素数量没有超过数组的长度,则不进行扩容。
方法ensureExplicitCapacity()结束。紧跟着
ensureCapacityInternal()方法结束。最后进入add方法执行
elementData[size++] = e;
如果数组元素数量超过数组的长度,则进行扩容,调用grow();方法。
grow(minCapacity);方法
Arrays.copyOf();方法
可以看出 扩容需要复制 之前的所有的数组元素。所以说扩容性能很差。
到这里add()方法就结束了。
public void add(int index, E element) { }
可以看出 add(int index, E element) 方法在index位置插入数据后,index后面的所有元素都要向右移动一位。这会导致性能下降。index的值越小。数组元素越多。则性能越低。如果index正好等于size,则和add()方法花费 的时间一样。
rangeCheckForAdd()
add(int index, E element) 结束
删
public E remove(int index) {}
public boolean remove(Object o) {}
根据传入参数删除数据。
如果传入参数 为null,则for循环遍历数组,找到第一个为null的数组,获取下标值。然后通过fastRemove(index) 删除数据。
如果传入参数不为null 则for循环遍历数组,通过equals方法判断数组的数据是不是和传入数据相等。然后获取下标值 通过fastRemove(index)删除数据。
private void fastRemove(int index) {}
删除方法结束
改
public E set(int index, E element) {}
1,检查下标是否超出范围
2,拿到下标为index的旧数据
3,把下标为index设置成新数据element
4,返回旧数据
查
public E get(int index) {}
1,检查下标 范围
2,返回数据
ArrayList的时间复杂度
可以看出ArrayList的 add ,remove 涉及到底层数组的复制,所以效率低。
而 Set/add 方法效率较高。
ArrayList存/取元素效率非常的高(get/set),时间复杂度是O(1),而查找,插入和删除元素效率不太高,时间复杂度为O(n)