ArrayList中add()方法的执行过程

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

ArrayList中add()方法的执行过程

步骤概述

ArrayList底层实现本质上是在对一个数组进行操作。

在对某个ArrayList实例对象执行add(E e)方法时,新增的元素将被追加到该实例的末尾,其内部的执行逻辑大致如下:

  1. 调用add(E e)方法

  2. 获取当前ArrayList实例的元素个数:size

  3. 计算新元素追加完成后的实例应该具备的最小容量:minCapacity = size + 1

  4. 获取当前ArrayList实例elementData

    • 如果该实例为new时创建的默认对象,获取一个默认的初始容量DEFAULT_CAPACITY=10
    • 如果该实例非新构建的对象,则获取该实例追加元素后应该具备的最小容量minCapacity
  5. 元素追加 或 进行扩容+数据迁移操作

    • 追加元素后应该具备的最小容量minCapacity < 当前集合容量elementData.length执行数据追加操作,直接跳到第8步

    • 集合刚被创建时minCapacity > elementData.length执行扩容+数据迁移操作

    • 追加元素时如果minCapacity > elementData.length执行扩容操作+数组迁移操作

  6. 执行扩容操作,返回扩容后容量

    • 先将容量扩容到当前容量的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
  7. 执行Arrays.copyOf(elementData, newCapacity)操作,复制当前实例对象到新的数组

  8. 执行新元素的追加操作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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值