ArrayList 作为 Java 集合框架中最基础且常用的动态数组实现,其内部通过对数组的精细化管理,实现了动态扩容、高效元素操作等核心能力。本文将从源码层面深入拆解 ArrayList 的扩容策略、索引查询、拷贝机制、哈希计算、元素删除、批量操作等关键模块,揭示其设计逻辑与性能优化细节。
- 动态扩容:通过 1.5 倍增长系数平衡内存与效率;
- 高效数组操作 :依赖
System.arraycopy实现元素移动与拷贝,利用 native 方法提升性能; - 浅拷贝策略:在数据隔离与内存复用间取得平衡;
- fail-fast 机制 :通过
modCount检测并发修改,保证数据一致性。
动态扩容
ArrayList 的扩容是其区别于普通数组的核心特性,grow(int minCapacity)方法根据数组初始化状态,采用差异化的扩容逻辑,平衡内存占用与操作效率。
ArrayList 通过grow(int minCapacity)方法实现动态扩容,根据数组初始化状态采取差异化策略:
java
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* 最小增长量 */
oldCapacity >> 1 /* 首选增长量(原容量的1/2) */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
- 指定初始容量场景 :按原容量的 1.5 倍扩容(
oldCapacity >> 1等价于除以 2),兼顾内存利用率与扩容效率; - 默认空数组场景 :首次扩容直接使用
DEFAULT_CAPACITY(默认 10)或所需最小容量的较大值,避免频繁扩容。
索引查询
ArrayList 通过分层设计的查询方法,实现从 "存在性检查" 到 "精准索引定位" 的全场景支持,核心依赖遍历与 null 值兼容逻辑。
ArrayList 提供多层次索引查询方法,兼顾通用性与效率:
存在性检查
java
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
首次 / 末次索引查询
java
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
public int lastIndexOf(Object o) {
return lastIndexOfRange(o, 0, size);
}
范围查询
java
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) return i;
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) return i;
}
}
return -1;
}
- null 值处理:单独判断避免空指针异常;
- 遍历策略:正序 / 逆序遍历分别适配首次 / 末次查询需求;
- 性能特性:时间复杂度 O (n),适用于中小规模数据查询。
拷贝
ArrayList 的拷贝操作通过**clone()与toArray()**实现,均采用浅拷贝策略:
java
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size); // 数组结构拷贝
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
拷贝特性
- 物理分离 :新 ArrayList 的
elementData是独立数组,修改新数组结构(如 add/remove)不会影响原数组; - 逻辑共享 :数组元素为对象引用时,新旧数组指向同一对象实例,修改元素属性(如
user.setName("new"))会双向影响; - 应用场景:适合快速复制数组结构,无需深拷贝元素的场景(如临时数据处理)。
哈希计算
ArrayList 的哈希计算遵循 Java 集合框架标准,通过累加策略保证顺序敏感性:
java
int hashCodeRange(int from, int to) {
final Object[] es = elementData;
int hashCode = 1;
for (int i = from; i < to; i++) {
Object e = es[i];
hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
}
return hashCode;
}
优势
- 31 的选择 :
- 31 是奇素数,素数特性减少哈希冲突(若用合数,哈希值会被因子整除,分布更集中);
- 31 可通过移位优化计算:
31 * i = (i << 5) - i(左移 5 位等价于乘以 32,减 i 即乘以 31),提升效率。
- 顺序敏感性 :累加方式使元素顺序影响最终哈希值(如
[a,b]与[b,a]哈希值不同),符合 List "有序集合" 的特性; - null 兼容:null 元素哈希值记为 0,避免空指针异常。
元素删除
ArrayList 的删除操作通过fastRemove()与shiftTailOverGap()实现,核心依赖System.arraycopy优化性能:
单个元素删除
java
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null; // 置null便于GC回收
}
范围元素删除
java
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
性能优化点
- 批量移动 :通过
System.arraycopynative 方法实现高效内存块复制; - 空间回收:删除后置 null 操作帮助 JVM 进行垃圾回收;
- 时间复杂度:单次删除为 O (n),批量删除通过遍历优化减少移动次数。
批量操作
ArrayList 通过**batchRemove()统一实现removeAll()与retainAll()**:
java
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
int r;
// 定位首个需要保留/删除的元素
for (r = from;; r++) {
if (r == end) return false;
if (c.contains(es[r]) != complement) break;
}
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e; // 原地覆盖保留元素
} catch (Throwable ex) {
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end); // 清理尾部无效元素
}
return true;
}
- 原地操作:通过覆盖写入减少数组复制开销;
- 异常安全:catch 块保证异常时数据完整性;
- 通用设计 :通过
complement参数切换删除 / 保留逻辑,复用代码逻辑。
- complement 参数 :
false(removeAll):保留c.contains(es[r]) == false的元素(删除交集);true(retainAll):保留c.contains(es[r]) == true的元素(保留交集)。
- 原地覆盖 :通过写指针
w在原数组上覆盖元素,避免创建新数组,节省内存; - 异常安全:catch 块保证遍历过程中出现异常时,未处理元素仍能正确复制,避免数据丢失。
批量添加
ArrayList 支持尾部追加与指定位置插入两种批量添加方式:
尾部追加
java
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0) return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
指定位置插入
java
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
// 扩容检查...
int numMoved = s - index;
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew, numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size = s + numNew;
return true;
}