jdk17的ArrayList的扩容机制


前言

本文简述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

  • 右移一位:
  1. 10转为二进制:1010
  2. 右移 1010 >>1 = 0101
  3. 0101转为十进制:5
  • 相加:
    10+5=15,即10的1.5倍

总结

  • 调用add方法后,会检测数组大小是否充足,如不充足,则调用grow方法进行扩容
  • 第一次调用add方法(数组长度为0)时,设置为默认长度(10)与最小所需的长度较大的一个
  • 再次调用add方法,最小所需的长度与数组当前长度加当前长度右移一位的值(增加为之前的1.5倍)选取一个较大的值作为数组长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值