ArrayList的add方法底层实现原理

ArrayList 的底层确实是用数组实现的 ,并且它的 add() 方法的核心逻辑就依赖于这个内部数组。下面我们从源码角度(以 Java 8+ 为主)来详细解析:

1. ArrayList 底层结构

scala 复制代码
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable {
    
    // 存储元素的底层数组(JDK 8+ 是 Object[],早期版本可能为 transient)
    transient Object[] elementData;

    // 实际元素个数
    private int size;
}
  • elementData 就是 ArrayList 内部用来存数据的动态数组
  • 初始容量默认为 10(如果你用无参构造函数)。

2. add(E e) 方法的核心流程

arduino 复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保容量足够
    elementData[size++] = e;           // 添加元素并增加 size
    return true;
}

关键在 ensureCapacityInternal() ------ 它负责扩容检查

3. 扩容机制(核心!)

步骤分解:

(1) ensureCapacityInternal(minCapacity)

ini 复制代码
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 如果是默认空数组,则至少扩容到 DEFAULT_CAPACITY = 10
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

(2) ensureExplicitCapacity(minCapacity)

scss 复制代码
private void ensureExplicitCapacity(int minCapacity) {
    modCount++; // 用于 fail-fast 机制
    if (minCapacity - elementData.length > 0)
        grow(minCapacity); // 需要扩容!
}

(3) grow(int minCapacity) ------ 真正的扩容逻辑

ini 复制代码
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 新容量 = 旧容量 + 旧容量 >> 1 (即 1.5 倍)
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // 如果 1.5 倍还不够(比如批量添加大量元素),就用 minCapacity
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    
    // 处理超大数组(接近 Integer.MAX_VALUE)
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // 创建新数组,并复制旧数据(调用 Arrays.copyOf)
    elementData = Arrays.copyOf(elementData, newCapacity);
}

4. 关键点总结

特性 说明
底层结构 Object[] elementData 数组
初始容量 无参构造时为 0 (延迟初始化),第一次 add 时扩到 10
扩容策略 1.5 倍增长old + old >> 1
扩容操作 调用 Arrays.copyOf() 创建新数组并复制元素(时间复杂度 O(n)
线程安全 ArrayList 不是线程安全的

举例:

当前容量 10,添加第 11 个元素 → 扩容到 10 + 5 = 15

再添加到第 16 个 → 扩容到 15 + 7 = 22(因为 15 >> 1 = 7

对比:ArrayList vs 普通数组

特性 普通数组 ArrayList
大小固定? ✅ 是 ❌ 动态扩容
泛型支持?
提供丰富方法? ✅(add/remove/contains等)
内存开销 稍大(对象头 + size 字段等)

📌 总结

  • ArrayList 底层是 Object[] 数组
  • add() 方法会检查容量,不够就扩容
  • 扩容规则:1.5 倍(右移一位加原值)
  • 扩容本质是创建新数组 + 复制旧数据(Arrays.copyOf ,所以频繁扩容会影响性能 → 建议预估容量并用 new ArrayList<>(initialCapacity) 初始化。
相关推荐
明月_清风4 小时前
加密解密系统完全指南:原理剖析与 Go 实践
后端
小江的记录本5 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:CMS:核心原理、回收流程、优缺点、废弃原因(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·面试·maven
冬奇Lab6 小时前
每日一个开源项目(第105篇):Twenty - 跳出 Salesforce 的圈套,定义现代开源 CRM
前端·后端·开源
ServBay7 小时前
月之暗面 Kimi Code 0.4.0 发布,终端 AI 编码助手全面采用 TypeScript,实现毫秒级启动
后端·aigc·ai编程
小江的记录本7 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
小江的记录本8 小时前
【JVM虚拟机】垃圾回收GC:垃圾收集器:G1:Region分区、Mixed GC、回收流程、适用场景(高频)(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·spring·spring cloud·面试
欧雷殿9 小时前
从「吸引子引导工程」看我的「一人公司」实践
前端·人工智能·后端
卷无止境9 小时前
用一个电影院售票厅,把 SimPy 的条件事件讲透
后端
日月云棠9 小时前
9 Double 与 Float —— IEEE 754 浮点数在 Java 中的实现
java·后端