一句话说透Java里面的ArrayList的工作原理和实现

217 阅读3分钟

ArrayList 就像 自动扩容的「动态数组」,底层用数组存数据,但比普通数组更智能——空间不够时自动扩容中间插入或删除时自动挪位置。用大白话拆解它的核心机制:


一、底层结构:数组 + 扩容策略

  • 核心数组Object[] elementData,实际存数据的地方。
  • 初始容量:默认10(但第一次添加元素时才真正分配空间)。
  • 扩容规则:空间不足时,新容量 = 旧容量 × 1.5(比如10 → 15 → 22 ...)。
  • 扩容代价:每次扩容都要复制旧数组到新数组,频繁扩容会影响性能(所以最好预估大小提前设置容量)。

二、核心操作原理

1. 添加元素(add()
  • 流程

    1. 检查当前数组是否已满。

    2. 如果满了

      • 创建新数组(大小为旧数组的1.5倍)。
      • 把旧数组的数据复制到新数组。
    3. 将新元素放入数组末尾的空位。

  • 源码片段

    public boolean add(E e) {  
        ensureCapacityInternal(size + 1); // 检查扩容  
        elementData[size++] = e; // 放入数组末尾  
        return true;  
    }  
    
2. 中间插入元素(add(index, element)
  • 流程

    1. 检查扩容。
    2. 把插入位置后的元素整体右移一位(类似排队时有人插队,后面的人后退)。
    3. 放入新元素。
  • 时间复杂度:O(n)(移动元素效率低,尽量避免频繁中间插入)。

3. 删除元素(remove(index)
  • 流程

    1. 把删除位置后的元素整体左移一位。
    2. 数组末尾置空(帮助GC回收)。
  • 示例

    ArrayList<String> list = new ArrayList<>(Arrays.asList("A", "B", "C", "D"));  
    list.remove(1); // 删除"B"  
    // 删除后数组变成:["A", "C", "D", null]  
    
4. 访问元素(get(index)
  • 直接通过下标访问数组:时间复杂度 O(1)(这是 ArrayList 的最大优势)。

    String element = list.get(2); // 直接取数组的第3个位置  
    

三、关键源码分析

1. 扩容实现(grow()方法)
private void grow(int minCapacity) {  
    int oldCapacity = elementData.length;  
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 新容量 = 旧容量 × 1.5  
    if (newCapacity < minCapacity) {  
        newCapacity = minCapacity;  
    }  
    elementData = Arrays.copyOf(elementData, newCapacity); // 复制旧数组到新数组  
}  
2. 中间插入的代价
public void add(int index, E element) {  
    rangeCheckForAdd(index); // 检查索引是否越界  
    ensureCapacityInternal(size + 1); // 检查扩容  
    // 把index后的元素右移一位(System.arraycopy)  
    System.arraycopy(elementData, index, elementData, index + 1, size - index);  
    elementData[index] = element;  
    size++;  
}  

四、优缺点总结

优点缺点
随机访问极快(O(1))中间插入/删除慢(O(n))
内存连续,缓存友好扩容有性能开销
代码简单易用非线程安全

五、使用建议

  1. 提前设置容量:如果知道数据量,构造时指定初始容量(new ArrayList<>(1000)),避免多次扩容。
  2. 避免频繁中间操作:中间插入/删除多用 LinkedList
  3. 多线程场景:用 Collections.synchronizedList 包装,或用 CopyOnWriteArrayList

六、经典对比:ArrayList vs 数组

对比项ArrayList数组
容量动态扩容固定长度
功能自带增删查改方法需手动实现逻辑
类型安全支持泛型(编译时检查类型)需运行时检查类型
性能扩容和中间操作有开销无额外开销

总结口诀
「ArrayList 动态数组,自动扩容真省心
随机访问快如箭,中间增删慢吞吞
提前设容是王道,多线程要加锁稳!」