主题切换
源码分析
一、ArrayList核心成员变量
java
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空实例共享的空数组(无参构造使用)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认大小空实例共享的空数组(无参构造延迟初始化使用)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储元素的数组缓冲区,ArrayList的容量就是该数组的长度
transient Object[] elementData;
// 集合中实际元素的数量
private int size;二、ArrayList构造方法
1. 无参构造
java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}- 初始化时不直接创建长度为10的数组,而是使用共享空数组
- 延迟初始化:在第一次调用
add()方法时,才会扩容到默认容量10
2. 带初始容量构造
java
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时,使用共享空数组
3. 集合构造
java
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
elementData = EMPTY_ELEMENTDATA;
}
}- 将传入的集合转换为数组,赋值给
elementData - 若集合为空,使用共享空数组
三、ArrayList添加与扩容流程
1. add()方法核心流程
java
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}ensureCapacityInternal(size + 1):确保数组容量足够elementData[size++] = e:将元素存入数组,size自增
2. 确保内部容量
java
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}- 第一次添加元素时,
elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,计算容量为max(10, 1)=10 - 后续添加时,直接返回
minCapacity
3. 确保显式容量
java
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}- 若
minCapacity > elementData.length,说明容量不足,调用grow()方法扩容
4. grow()扩容方法
java
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 扩容为原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 数组拷贝,扩容后创建新数组并复制旧数据
elementData = Arrays.copyOf(elementData, newCapacity);
}- 扩容规则:新容量 = 原容量 + 原容量/2(即原容量的1.5倍)
- 若扩容后的容量仍小于
minCapacity,则使用minCapacity作为新容量 - 通过
Arrays.copyOf()创建新数组,复制旧数组数据
四、关键场景分析
1. 第一次添加元素
- 初始状态:
elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(空数组) - 调用
add(1)时,calculateCapacity返回10,ensureExplicitCapacity触发扩容 grow()方法将数组扩容到10,元素存入数组,size变为1
2. 第2-10次添加元素
- 数组容量为10,
minCapacity为2-10,均不超过数组长度,无需扩容 - 直接将元素存入数组,size自增
3. 第11次添加元素
minCapacity = 11,大于数组长度10,触发扩容- 新容量 = 10 + 10/2 = 15,数组扩容到15,元素存入数组,size变为11
五、面试相关问题
问:ArrayList的底层实现原理是什么?答: ArrayList基于动态数组实现,通过
elementData数组存储元素,初始时使用共享空数组延迟初始化。添加元素时,若数组容量不足则触发扩容,每次扩容为原容量的1.5倍,并通过Arrays.copyOf()复制旧数据到新数组。问:ArrayList list = new ArrayList(10) 中的list扩容几次?答: 不会触发自动扩容。因为指定初始容量为10,
elementData直接创建长度为10的数组,添加前10个元素时无需扩容;当添加第11个元素时,才会触发第一次扩容,容量变为15。问:ArrayList和LinkedList的区别是什么?答:
- 底层结构:ArrayList基于动态数组,LinkedList基于双向链表;
- 访问效率:ArrayList随机访问效率高(O(1)),LinkedList需遍历查找(O(n));
- 增删效率:ArrayList在中间增删时需移动元素(O(n)),LinkedList在已知节点前后增删效率高(O(1));
- 内存占用:ArrayList扩容会浪费部分空间,LinkedList每个节点需额外存储前后指针,内存开销更大。
问:ArrayList的扩容机制是什么?答: ArrayList默认初始容量为10(无参构造延迟初始化),当元素数量超过数组长度时触发扩容,新容量为原容量的1.5倍。扩容通过
Arrays.copyOf()创建新数组并复制旧数据,因此扩容操作开销较大,建议预估数据量并指定初始容量,减少扩容次数。