1、ArrayList简介
ArrayList 的底层是动态数组transient Object[] elementData;因为支持扩容,所以它是动态数组。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
- 它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。 在我们学数据结构的时候就知道了线性表的顺序存储,插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1)
- ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
- ArrayList 实现了RandomAccess 接口, RandomAccess 是一个标志接口,实现这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
- ArrayList 实现了Cloneable 接口,覆盖了函数 clone(),能被克隆。
- ArrayList 实现java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。
2、源码解读
2.1 构造函数
//若初始化时不指定长度,默认是10
private static final int DEFAULT_CAPACITY = 10;
//默认的空数组,用于空参构造函数,当添加第一个元素后,数组长度变为10
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//空数组,用于参数为0的构造函数
private static final Object[] EMPTY_ELEMENTDATA = {};
//空参构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带参构造函数(参数是数组的初始容量,等于0时和空参构造函数别无二致,小于0时抛出异常)
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.2 ensureCapacityInternal 及其相关
这两个函数的意义在于,当我们添加某个元素时,有可能会出现
//数组元素个数 区别于数组长度!!
private int size;
//数组缓冲,真正存放数据的地方,当第一个元素加入数组后,数组长度自动由0扩充到10(空参构造的情况下)
transient Object[] elementData;
//该函数用于判断当前数组是否能满足minCapacity容量的储存
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//当数组是经过空参构造初始化的,则返回10,否则返回minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//判断我们目前需要的最小容量是否大于数组缓冲的长度,是的话进行扩容grow。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
2.3 add(E e)
//先确保数组缓冲的容量是否充足,即当我们添加一个元素时,需要 当前元素个数+1 的空间,结合上面的函数
//来看,如果当前元素个数+1不比当前数组缓冲的长度大,则不进行扩容,否则进行扩容
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
2.4 add(int index, E element)
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//拷贝 索引index开始到结束的元素 到 index+1到末尾的各个位置上
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将该索引的元素置为element
elementData[index] = element;
size++;
}
//这个函数主要是查看插入索引是否是非法的,即 >size 或 <0 的两种情况
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.5 rangeCheck(int index)
//检查索引是否合法,如果索引>=数组长度,则抛出IndexOutOfBoundsException异常private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.6 get(int index)及其相关
E elementData(int index) {
return (E) elementData[index];
}
//先检查index是否合法,合法则返回相应的元素,不合法抛出异常
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
2.7 grow(int minCapacity)及其相关
private void grow(int minCapacity) {
// overflow-conscious code
//原始数组长度
int oldCapacity = elementData.length;
//新的长度: 原始长度+原始长度右移1位
//对于偶数的原始长度来说是1.5倍,但对于奇数来说,应该说是近似1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新的长度比需要的最小长度还小,那我们令新的长度为最小长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新的长度比最大的数组长度MAX_ARRAY_SIZE还大,这时可能是因为需要的最小长度minCapacity为负数了
//即发生溢出情况,抛出内存错误;也有可能是因为需要的最小长度确实>MAX_ARRAY_SIZE
//那么这种情况,新的长度为MAX_ARRAY_SIZE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//最后用Arrays.copyOf复制旧数组中的数据
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//Arrays.copyOf方法
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
3. 其他内容
3.1 System.arraycopy()和Arrays.copyOf()方法
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
从上面可以看出,Arrays.copyOf()其实还是调用了System.arraycopy,在copyOf中,会新建一个数组并返回
而System.arraycopy需要原数组和目标数组,同时需要他们的开始索引及复制的元素个数