在Java中,ArrayList是一个可以动态扩容的数组,其底层实现是基于数组而设计的。当ArrayList的容量不足以存储新的元素时,就需要进行扩容操作。本文将详细讲解在Java中ArrayList集合底层的扩容原理。
ArrayList内部数组实现
首先,我们需要了解ArrayList内部数组的实现方式。在ArrayList中,用于存储元素的是一个Object类型的数组。ArrayList的数据结构定义如下:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//...
transient Object[] elementData;
//...
}
上述定义中,elementData
是Object类型数组,用于存储元素。
数据元素的添加与扩容
ArrayList在添加元素时,会先检查数组是否已满。如果数组已满,则需要进行扩容。扩容的过程如下:
- 如果当前数组容量已经超过最大容量,就抛出OutOfMemoryError;
-
如果当前容量小于最大容量,则进行如下操作:
-
计算出新的容量值newCapacity。
- 创建一个新的数组newElementData,其长度为newCapacity。
- 将原数组elementData中所有元素复制到新数组newElementData中。
- 将ArrayList的elementData引用指向新的数组newElementData。
新的数组长度一般是原来长度的1.5倍(可以通过修改DEFAULT_CAPACITY
和DEFAULT_CAPACITY_INCREMENT
静态成员变量修改扩容触发条件)。以下示例演示了在ArrayList中添加元素所触发的容量扩容:
ArrayList<String> list = new ArrayList<>(10);
for (int i = 0; i < 15; i++) {
list.add(String.valueOf(i));
}
System.out.println(list.size()); // 15
上述代码中,我们首先创建了一个初始容量为10的ArrayList对象。接着,我们循环添加15个元素,此时就需要进行扩容操作。当添加第11个元素(即当前容量已满)时,ArrayList内部就会触发数组容量的扩容操作,扩容后的新容量会是原来容量的1.5倍,即15(因为10*1.5=15),然后再将元素添加到ArrayList中。
ArrayList扩容的复杂度
ArrayList的扩容操作所发生的时间复杂度不是常量级别的,而是O(n)。因为当数组需要扩容时,就需要将原有数组中的所有元素复制到新的数组中,这个过程的时间复杂度是O(n)。
虽然ArrayList的扩容时间复杂度为O(n),但由于扩容操作的触发是有条件的,并不是每次添加元素都会触发扩容操作。因此,我们在使用ArrayList时,要根据实际情况合理选择ArrayList的初始容量,在尽可能降低扩容次数的同时,又不会浪费太多内存空间。
以上就是Java中ArrayList集合底层扩容原理的完整攻略。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:在java中ArrayList集合底层的扩容原理 - Python技术站