Java 中ArrayList的扩容原理是在其内部数组无法容纳更多元素时,会创建一个新的更大的数组,并将原有数组中的所有元素复制到新数组中。具体过程如下:

  1. 初始容量与默认容量

    • ArrayList在初始化时不指定大小的情况下,默认容量为 10(即DEFAULT_CAPACITY = 10)。
    • 当使用无参构造函数创建一个空ArrayList时,它会初始化一个空数组引用。
  2. 添加元素与自动扩容

    • 当通过add()方法添加元素,且当前存储元素的实际数量(size)等于当前数组的容量时,需要进行扩容操作。
    • 扩容机制通常是将现有容量翻倍,也就是新的容量为原来的两倍加上一定的增长量(通常就是原容量)。扩容计算公式可以是:newCapacity = oldCapacity + (oldCapacity >> 1) 或者 newCapacity = oldCapacity * 1.5 + 1(取决于不同的 Java 版本实现),这确保了随着元素数量的增长,扩容后的空间足够容纳现有的元素和一些额外的新元素,从而减少频繁扩容的可能性。
  3. 扩容限制

    • 扩容不是无限增长的,当容量达到一定阈值后,扩容策略会发生变化以防止无界增长。在 Java 的ArrayList实现中,最大容量是Integer.MAX_VALUE - 8,接近于Integer.MAX_VALUE,因为数组索引必须是非负整数,而且还需要留出一些空间来避免整数溢出问题。
    • 当容量到达最大值时,扩容将不再增加容量,后续添加元素的操作将会抛出OutOfMemoryError异常。
  4. 元素迁移

    • 在确定了新的容量之后,ArrayList会创建一个新的数组,然后遍历旧数组并将所有元素复制到新数组中。
    • 这个过程中涉及到了大量的元素复制操作,因此对于大型列表来说,扩容是一个相对耗时的过程。
  5. 更新引用

    • 当元素迁移完成后,ArrayList会丢弃旧数组,并将内部变量指向新数组,此后对集合的所有操作都在这个新数组上进行。

综上所述,ArrayList的扩容原理主要是基于动态扩展底层数据结构——数组的容量,在保证线程不安全的前提下,通过合理的扩容策略和数组复制操作来适应不断增长的数据存储需求。