ArrayList
是Java集合框架中最常用的动态数组实现之一,它提供了动态扩容、随机访问等特性,适用于大多数场景。
ArrayList
实现了List
接口,基于动态数组实现。它允许存储任意类型的元素,并根据需要动态调整容量。在添加、删除元素时,ArrayList
会自动进行扩容和缩容操作。
为保证效率,ArrayList没有实现同步(synchronized)为非线程安全集合,如需并发访问,用户需手动同步,也可使用Vector代替。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// ...
}
ArrayList
继承自AbstractList
,实现了List
接口。RandomAccess
接口,表示支持快速随机访问。Cloneable
接口,表示可以进行克隆。Serializable
接口,表示支持序列化。// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 用于空ArrayList实例的共享空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小的空ArrayList实例的共享空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储ArrayList元素的数组缓冲区
transient Object[] elementData;
// ArrayList实际元素数量
private int size;
// 无参构造方法,创建一个空的ArrayList
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 指定初始容量的构造方法
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);
}
}
ArrayList
在添加元素时会自动进行扩容,其扩容机制如下:
// 添加元素
public boolean add(E e) {
// 确保容量足够
ensureCapacityInternal(size + 1);
// 将元素添加到数组末尾
elementData[size++] = e;
return true;
}
// 确保容量足够
private void ensureCapacityInternal(int minCapacity) {
// 扩容
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果数组是空数组,取默认容量和minCapacity的较大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 扩容
private void ensureExplicitCapacity(int minCapacity) {
// 记录修改次数
modCount++;
// 如果minCapacity大于数组容量,需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容策略
private void grow(int minCapacity) {
// 当前数组容量
int oldCapacity = elementData.length;
// 新数组容量,扩容为原容量的1.5倍 oldCapacity >> 1 右移:向右除以2的移动位数次幂
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量仍然小于minCapacity,则使用minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量超过最大数组容量,使用Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 复制数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
注意:
ArrayList的自动扩容时会将老数组中的元素重新拷贝一份到新的数组中,并且扩容的大小为原容量的1.5倍。
在创建ArrayList
时,如果能够预估元素的数量,最好通过构造方法指定初始容量,避免动态扩容带来的性能开销。
List<String> list = new ArrayList<>(1000);
如果事先知道要添加的元素数量很大,最好在创建ArrayList
时设置一个足够大的初始容量,避免多次扩容。
List<String> list = new ArrayList<>(10000);
list.addAll(anotherList);
根据实际需求,通过调用ensureCapacity
方法来手动增加ArrayList实例的容量
public void ensureCapacity(int minCapacity) {
// 获取最小容量
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
? 0
: DEFAULT_CAPACITY;
// 如果minCapacity大于数组最小容量,则进行扩容
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
在遍历ArrayList
时,最好使用Iterator
而不是通过索引访问元素。因为通过索引访问元素的方式在LinkedList
上效率较高,但在ArrayList
上会产生不必要的性能开销。
List<String> list = new ArrayList<>();
// 不推荐
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 推荐
for (String s : list) {
System.out.println(s);
}
在ArrayList
上使用subList
方法生成子列表时,尽量避免在大量数据上进行截取。因为subList
方法返回的子列表并不是一个新的ArrayList
,而是原列表的一个视图,操作子列表可能会影响到原列表。
List<String> list = new ArrayList<>();
List<String> subList = list.subList(0, 10);