1. 底层数据结构
-
核心是一个
Object[]
数组:
ArrayList
内部维护一个Object
类型的数组elementData
,用于存储所有元素。transient Object[] elementData; // 实际存储元素的数组
-
默认初始容量:
-
默认初始容量为 10(通过无参构造器创建时)。
-
可指定初始容量(
new ArrayList<>(100)
),避免频繁扩容。
-
2. 动态扩容机制
扩容触发条件
当添加元素(add()
)时,如果当前数组已满(size == elementData.length
),会自动触发扩容。
扩容规则
-
计算新容量:
新容量一般为旧容量的 1.5 倍(通过位运算oldCapacity + (oldCapacity >> 1)
实现)。int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍扩容
-
处理最小扩容需求:
如果用户一次添加多个元素(如addAll()
),新容量可能需更大,取需求容量
和1.5 倍旧容量
的较大值。 -
数组拷贝:
通过Arrays.copyOf()
创建一个新数组,并将旧数组数据复制到新数组中。elementData = Arrays.copyOf(elementData, newCapacity);
示例
-
初始容量为 10,添加第 11 个元素时触发扩容 → 新容量为 15。
-
继续添加到第 16 个元素 → 再次扩容到 22(15 × 1.5 = 22.5,向下取整为 22)。
3. 基本操作原理
添加元素(add(E e)
)
-
直接追加:
时间复杂度 O(1)(不触发扩容时)。 -
扩容时:
时间复杂度 O(n)(需复制数组),但分摊时间复杂度仍为 O(1)。
插入元素(add(int index, E e)
)
-
移动元素:
将插入位置后的元素向后移动一位(通过System.arraycopy()
)。System.arraycopy(elementData, index, elementData, index + 1, size - index);
-
时间复杂度:
O(n)(最坏情况下需移动所有元素)。
删除元素(remove(int index)
)
-
移动元素:
将删除位置后的元素向前移动一位。System.arraycopy(elementData, index + 1, elementData, index, size - index - 1);
-
时间复杂度:
O(n)(最坏情况下需移动所有元素)。
查询元素(get(int index)
)
-
直接访问数组:
return (E) elementData[index]; // 时间复杂度 O(1)
4. 关键特性
优点
-
动态扩容:无需手动处理数组扩容,适合数据量不确定的场景。
-
随机访问高效:通过索引直接访问元素,时间复杂度 O(1)。
-
内存紧凑:连续存储,缓存友好,适合遍历操作。
-
兼容泛型:支持泛型(如
ArrayList<String>
),类型安全。
缺点
-
插入/删除效率低:需移动元素,时间复杂度 O(n)。
-
内存浪费:扩容机制可能导致未使用的预留空间(如容量为 15,实际只存 11 个元素)。
-
非线程安全:多线程环境下需手动同步或改用
Vector
(性能更低)。
5. 与普通数组的对比
特性 | 普通数组 | ArrayList |
---|---|---|
长度固定 | 初始化后长度不可变 | 动态扩容 |
类型 | 可以是基本类型(如 int[] ) | 只能存储对象(需装箱/拆箱) |
内存管理 | 无额外开销 | 有容量预留(可能浪费内存) |
功能扩展 | 无内置方法 | 提供丰富 API(如排序、遍历等) |
6. 源码设计细节
-
优化删除操作:
删除元素后,将最后一个位置置为null
(帮助垃圾回收)。elementData[--size] = null; // 显式清除引用
-
快速失败机制(Fail-Fast):
通过modCount
记录修改次数,迭代时检测并发修改并抛出ConcurrentModificationException
。 -
序列化优化:
elementData
标记为transient
,序列化时仅写入实际元素(避免序列化空余容量)。
适用场景
-
高频随机访问:如通过索引快速读取数据。
-
数据量动态变化:需要自动扩容的场景。
-
顺序遍历操作:如批量处理数据。
总结
ArrayList
通过动态数组的机制,在普通数组的基础上实现了灵活性和功能扩展:
-
动态扩容:1.5 倍增长策略平衡了内存和性能。
-
高效随机访问:底层数组实现 O(1) 的索引访问。
-
功能丰富:提供增删改查、排序、遍历等 API。
但其插入/删除效率低的缺点,在需要频繁修改数据的场景下,可改用 LinkedList
(基于链表)。