ArrayList的扩容机制 (JDK1.8 源码解析)

ArrayList

java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。


ArrayList的三种构造方法

ArrayList的三种构造方法


ArrayList的扩容机制(源码)

/**
* ArrayList主要成员变量
*/
private static final int DEFAULT_CAPACITY = 10;
// 定义一个空数组,用来判断ArrayList第一次添加数据的时候要扩容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 数组的容量,第一次添加数据时,容量扩为10
transient Object[] elementData;
// 当前数组的长度
private int size;
/**
* 构造一个初始容量为10的空列表。
*/
public ArrayList() {
	Javathis.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 共享的空数组实例,用于默认大小的空实例(通过无参构造器构造出来的是长度为0的数组)
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

构造出来的是长度为0的数组,但初始容量为10,为什么?(通过扩容实现)

以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。

1、add()

/**
* 将指定的元素追加到此列表的末尾
* JDK11将add方法进行了简化
*/
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 增加modCount!!
    elementData[size++] = e;
    // 只要调用方法就一定返回true
    return true;	
}

2、ensureCapacityInternal()

/**
* 在使用add方法前,先调用了ensureCapacityInternal方法
* 获取最小容量
*/
private void ensureCapacityInternal(int minCapacity) {
    // add 第1个元素时,minCapacity=1
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 通过Math.max()方法比较后,minCapacity=10(初始容量变为10)
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

3、ensureExplicitCapacity()

/**
* 判断是否需要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
    // ArrayList扩容方法
    grow(minCapacity);
}
  • 当 add 第1个元素时,无参构造器构造出来的是长度为0的数组,即 elementData.length = 0;此时minCapaucity = 10,那么 minCapacity - elementData.length > 0 会返回true,执行grow()方法。
  • 当 add 第2个元素时,minCapacity = 2,此时的 elementData.length 在添加第一个元素后执行完group()方法,容量变成 10 。那么 minCapacity - elementData.length > 0 返回false,不扩容。
  • 目前的初始容量为10,此后一直添加至元素个数超过10,那么 minCapacity - elementData.length > 0 成立,再次调用grow()方法进行新的扩容。

4、grow()

/**
* 分配的最大数组大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
* ArrayList扩容方法
* 增加容量以确保它至少可以容纳最小容量参数指定的元素个数。
*/
private void grow(int minCapacity) {
    // overflow-conscious code
    // 首先获取数组的旧长度(即存满时的数组长度)
    int oldCapacity = elementData.length;
    // 二进制中,将 oldCapacity 右移一位,其效果相当于 oldCapacity/2,即新容量变为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 判断新容量是否大于需要的最小容量,如果小于,则返回 true,那么新容量就替换为所需要的最小容量
    if (newCapacity - minCapacity < 0)
  		newCapacity = minCapacity;
    // 判断新容量是否大于 MAX_ARRAY_SIZE,如果成立,调用 hugeCapacity(minCapacity) 方法
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    	newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

5、hugeCapacity()

private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) 
        // 内存溢出
		throw new OutOfMemoryError();
    // 对 minCapacity 和 MAX_ARRAY_SIZE 进行比较(即判断 Integer.MAX_VALUE - minCapacity < 8) 
	// 若 minCapacity大,Integer.MAX_VALUE则作为新数组的大小
	// 若 MAX_ARRAY_SIZE大,MAX_ARRAY_SIZE则作为新数组的大小
	// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	return (minCapacity > MAX_ARRAY_SIZE) ?
		Integer.MAX_VALUE :
		MAX_ARRAY_SIZE;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值