目录
前言
本文简述jdk17下的ArrayList集合的扩容机制,如有错误欢迎指正
一、成员变量
- int DEFAULT_CAPACITY ——默认初始容量
- Object[] EMPTY_ELEMENTDATA——用于空实例的共享空数组实例(空的)
- Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA——用于默认大小的空实例的共享空数组实例(默认大小的)
- Object[] elementData——存储 ArrayList 元素的数组缓冲区。 ArrayList 的容量就是这个数组缓冲区的长度。
- int size——ArrayList 的大小(它包含的元素数量)。
二、源码
1.调用add方法
add方法
代码如下:
public boolean add(E e) {
modCount++;//记录更改次数
add(e, elementData, size);//调用重载的add方法
return true;
}
调用重载的add方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)//判断当前大小是否等于元素长度
elementData = grow();
elementData[s] = e;//将数据填入
size = s + 1;//数组大小加1
}
,如果相等则数组长度不足(数组长度刚好足够容纳当前数量的元素,就将数组长度增长——保证数组长度至少大于元素个数一格)
2.扩容机制
grow方法:
代码如下:
private Object[] grow() {
return grow(size + 1);//调用重载的grow方法,参数为当前数组大小加1
}
private Object[] grow(int minCapacity)//将传入的参数标记为最小所需的长度 {
int oldCapacity = elementData.length;//当前长度设置为当前元素数量
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
//当前长度大于0 或 数组缓冲区不等于默认共享空数组实例(设置的长度)
//不是第一次调用add方法
{
int newCapacity =
//调用newLength方法
ArraysSupport.newLength(oldCapacity, /* 当前长度 */
minCapacity - oldCapacity, /* 最小增加量 */
oldCapacity >> 1 /*长度右移一位 首选增加量 */);
return elementData = Arrays.copyOf(elementData, newCapacity);//将数据复制道新数组中
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
如果:
当前长度大于0或数组缓冲区不等于默认共享空数组实例(设置的长度)(不是第一次调用add方法)
则:
新长度(newCapacity)使用newLength方法设置:
参数:
当前长度(oldCapacity),
最小所需的长度 减 当前长度(最小增加量),
当前长度右移一位(首选增长量)
返回:数组缓冲区使用copyOf方法设置
否则:返回数组缓冲区设置为默认长度与最小所需的长度较大的一个
newLength方法:
代码如下:
public static int newLength(
int oldLength,//数组的当前长度
int minGrowth,//最小所需增长量
int prefGrowth//首选增长量
) {
int prefLength //最终数组长度
= oldLength + Math.max(minGrowth, prefGrowth);
//数组的当前长度 + 最小所需增长量与首选增长量中较大的
// might overflow(可能溢出)
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
//最终数组长度大于0 且 小于等于int数据的上限-8
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
//单独处理
}
}
设置:
最终数组长度=当前长度+最小所需增长量与首选增长量中较大的一个
如果:
最终数组长度大于0且小于int数据的上限-8
则:
返回最终数组长度
否则:
单独处理——使用hugeLength方法设置新长度
单独处理
hugeLength方法
代码如下:
private static int hugeLength(
int oldLength, //当前长度
int minGrowth //最小增长量
) {
int minLength = oldLength + minGrowth;
//设置最小所需长度 为 数组当前长度 加 最小增长量
if (minLength < 0) { // overflow(可能溢出)
//报错
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
//最小所需长度未达上限-8
return SOFT_MAX_ARRAY_LENGTH;
//返回上限-8
} else {
return minLength;
}
}
如果:
最小所需长度小于0,则抛出OutOfMemoryError错误
否则如果:
最小所需长度小于等于int数据的上限-8,返回int数据的上限-8
否则(大于上限-8,小于上限):
返回最小所需长度
3.其他
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
public static final int MAX_VALUE = 0x7fffffff;//数据上限为2^31-1
4.长度右移一位的解释:
假设:当前长度:10
- 右移一位:
- 10转为二进制:1010
- 右移 1010 >>1 = 0101
- 0101转为十进制:5
- 相加:
10+5=15,即10的1.5倍
总结
- 调用add方法后,会检测数组大小是否充足,如不充足,则调用grow方法进行扩容
- 第一次调用add方法(数组长度为0)时,设置为默认长度(10)与最小所需的长度较大的一个
- 再次调用add方法,最小所需的长度与数组当前长度加当前长度右移一位的值(增加为之前的1.5倍)选取一个较大的值作为数组长度