ArrayList中add()方法的执行过程
步骤概述
ArrayList底层实现本质上是在对一个数组进行操作。
在对某个ArrayList实例对象执行add(E e)方法时,新增的元素将被追加到该实例的末尾,其内部的执行逻辑大致如下:
-
调用
add(E e)方法 -
获取当前ArrayList实例的元素个数:size
-
计算新元素追加完成后的实例应该具备的最小容量:minCapacity = size + 1
-
获取当前ArrayList实例
elementData- 如果该实例为
new时创建的默认对象,获取一个默认的初始容量DEFAULT_CAPACITY=10 - 如果该实例非新构建的对象,则获取该实例追加元素后应该具备的最小容量minCapacity
- 如果该实例为
-
元素追加 或 进行扩容+数据迁移操作
-
追加元素后应该具备的最小容量minCapacity < 当前集合容量elementData.length执行数据追加操作,直接跳到第8步
-
集合刚被创建时minCapacity > elementData.length执行扩容+数据迁移操作
-
追加元素时如果minCapacity > elementData.length执行扩容操作+数组迁移操作
-
-
执行扩容操作,返回扩容后容量
-
先将容量扩容到当前容量的1.5倍,判断是否够用:minCapacity < newCapacity
- 不够用将容量扩容为minCapacity
-
判断扩容后的容量是否超出了数组限定的最大容量MAX_ARRAY_SIZE= Integer.MAX_VALUE - 8
为什么数组的最大值是Integer.MAX_VALUE - 8?
数组作为一个对象,需要一定的内存存储对象头信息,对象头信息最大占用内存不可超过8字节。
如果数组长度过大,可能出现的两种错误
OutOfMemoryError: Java heap space 堆区内存不足(这个可以通过设置JVM参数 -Xmx 来指定)。
OutOfMemoryError: Requested array size exceeds VM limit 超过了JVM虚拟机的最大限制
- 扩容后容量未超出MAX_ARRAY_SIZE,返回扩容后容量newCapacity
- 扩容后容量超出MAX_ARRAY_SIZE,返回Integer.MAX_VALUE
-
-
执行
Arrays.copyOf(elementData, newCapacity)操作,复制当前实例对象到新的数组 -
执行新元素的追加操作
elementData[size++] = e,size在方法内自增
源码
/**
* 将指定的元素追加到此列表的末尾。
*/
public boolean add(E e) {
//扩容运算,原数据迁移
ensureCapacityInternal(size + 1);
//追加新元素,size自增
elementData[size++] = e;
return true;
}
..........此处省略无关代码..........
/**
* 得到一个初始容量或新元素添加完成后所需的最小容量
* @param elementData 当前实例对象
* @param minCapacity 所需的最小容量 = size + 1
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA是使用空参构造的默认空数组
// elementData如果与其相等,则当前实例创建后未进行过任何操作,此时返回默认容量10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(
calculateCapacity(elementData, minCapacity)
);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 容量溢出,进行扩容运算和原数据迁移
grow(minCapacity);
}
/**
* 要分配的数组的最大大小。
* 一些VM在数组中保留一些标题字
* 尝试分配更大的数组可能会导致OutOfMemoryError:请求的数组大小超出了VM限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 增加容量以确保它至少可以容纳最小容量参数指定的元素数。
*
* @param minCapacity 所需的最小容量
*/
private void grow(int minCapacity) {
// 先将当前实例的数组容量扩容到1.5倍
int oldCapacity = elementData.length; //当前容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //位移运算 >> 1 相当于除以2
// 扩容后不够用,将数组容量直接扩容到实际长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//还是不够用,扩容Integer.MAX_VALUE=2147483647
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容完成,复制当前实例数据到新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

本文详细解析了Java中ArrayList的add()方法执行流程,包括如何计算最小容量、判断是否需要扩容及扩容策略,深入探讨了ArrayList的底层实现机制。
7287

被折叠的 条评论
为什么被折叠?



