ArrayList
一、简介
ArrayList 底层的数据结构是 “数组”,它会随着元素的增加而动态扩容,它是有序可重复、线程不安全的集合,可存入多个 null 对象。
1.1 源码分析
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
从源码分析,ArrayList 继承于 AbstractList,实现了 List,RandomAccess,Cloneable 和 Serializable 接口:
-
实现 List,并继承 AbstractList,获得集合接口 List 的基础功能;
-
实现 RandomAccess ,获得了快速随机访问存储元素的功能(RandomAccess 是一个标记接口,没有任何方法);
// Collections类 public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) { if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD) return Collections.indexedBinarySearch(list, key); else return Collections.iteratorBinarySearch(list, key); } /** *区别: * 实现RandomAccess接口的List集合采用一般的for循环遍历,而未实现这接口则采用迭代器。 * ArrayList用for循环遍历比iterator迭代器遍历快,LinkedList用iterator迭代器遍历比for循环遍历快。 */
-
实现 Cloneable,重写 clone,实现克隆功能;
-
实现 Serializable,可被序列化。
1.2 使用建议
- 当集合中对插入元素数据的速度要求不高,但是要求快速访问元素数据,则使用ArrayList。
- 当需要运行在高版本的
jdk
环境下,经常采用尾部插入操作时,建议使用 ArrayList。
二、特点
2.1 初始化ArrayList的大小,未指定时默认是10
// 默认初始容量:10
private static final int DEFAULT_CAPACITY = 10;
// 容量最大值:2147483647
Integer.MAX_VALUE
2.2 数据都存储在数组中
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //用于校验是否为空数组
transient Object[] elementData;
2.3 扩容
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 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;
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
默认构造函数初始化空数组。在调用添加元素方法的时候,会初始大小为10的数组,再将元素存入数组中。不断存入元素会不断扩容,当 newCapacity 扩容量大于数组定义的最大阀值时,会调用 hugeCapacity 进行判断。如果待扩容大小已经超过 Integer 的最大值(溢出为负数),将抛出 OutOfMemoryError (内存溢出错误)。否则返回 Integer的最大值或 MAX_ARRAY_SIZE。故 ArrayList 的最大容量是 Integer 的最大值(-2的31次方~2的31次方减1)。
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);
}
}
- 当传入值 > 0 时,会创建指定容量大小的数组,之后会按照此大小 “理论的1.5倍” 扩容;
- 当传入值 = 0 时,那么前四次的扩容每次只加1,第五次之后的才按照 “理论的1.5倍” 扩容;
注意:
“理论的1.5倍” 扩容是指:扩容后的大小 = 原大小 + 原大小 / 2。如果 原大小 / 2 刚好约去小数部分,那么扩容的大小并没有原大小的1.5倍。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 指此时的 elementData数组是Object子类的数组,
// 若对数组中某一位置赋值Object父类时,将抛出向下转型异常,故单独判断不做拷贝
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
-
若 Collection.toArray 返回的是 Object 的数组:
将直接使用返回值(已将原数组中的数据浅拷贝到 elementData)。
-
若 Collection.toArray 返回的是 Object 子类的数组:
将调用 Arrays.copyOf(System.arraycopy)方式创建 “toArray” 大小的数组,并将原数组中的数据浅拷贝到 elementData。
因为数据存储在数组中,有些虚拟机会在数组对象中额外存储一些信息(如描述对象状态的信息、对象是否同步的信息、数组的大小等),单独判断一次是为了避免内存溢出。