ArrayList扩容源码分析

一、前言

我们都知道ArrayList底层是用一个数组来存储对象的,但是数组的长度是不可变的。当数组内的空间用完之后,ArrayList是如何扩容(即扩大数组长度)的?什么时候开始扩容的?本章就来探讨下这些问题。

二、关键属性

1. 存储数据的数组:Object[] elementData

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

这个肯定关键,没有这个数组ArrayList就没有办法存储数据了。

2. 当前数组中数据个数:int size

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

3. 空数组:DEFAULTCAPACITY_EMPTY_ELEMENTDATA

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

当使用ArrayList的无参构造函数时,会用到这个空数组,源码如下

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

看到此时elementData了么,是个空数组!不是传说中长度为10的数组哟
4. 空数组:EMPTY_ELEMENTDATA

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

仅当使用new ArrayList(0)的时候,会用到这个空数组,源码如下

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;// 就这个地方
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

5. 默认初始化大小(有坑哟,后面四中会提到)

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

6. 数组最大长度

	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

这个名字也有点歧义,后面会看到数组最大长度可以为:Integer.MAX_VALUE

三、扩容的时机

  1. 从ArrayList的add()方法说起
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 入参为需要的容量
        elementData[size++] = e;  // 把元素放到数据中
        return true;  // 返回插入成功
    }

add()方法中,简单三行代码,很明显扩容逻辑在第一行代码中,跟进去继续看。

  1. 计算最小容量(minCapacity)
    private void ensureCapacityInternal(int minCapacity) {
        // 看calculateCapacity()这个方法
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	// 计算需要的容量是多少
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    	// new ArrayList()的情况,见二、3
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // DEFAULT_CAPACITY的值为10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

从add()方法中可以看到,入参minCapacity的值为:size+1。所以calculateCapacity()方法的放回值有两种。

  • new ArrayList()创建的ArrayList:10和size+1谁大返回谁
  • 其他方式创建的ArrayList:返回size+1
    tip:扩容后的数组长度,与minCapacity的值息息相关!
  1. 何时扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 当最小需要的容量大于当前数组长度时,开始扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

四、扩容细节

  1. 扩容代码
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 新容量为原数组长度的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 原数组为0的情况
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf()方法,把原数组复制到新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    // newCapacity比最大值还要大的情况,根据minCapacity的值来获取新数组长度
    private static int hugeCapacity(int minCapacity) {
    	// minCapacity数值溢出,抛出异常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // minCapacity > MAX_ARRAY_SIZE时,数组扩容到Integer.MAX_VALUE
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
  1. 扩容分类(三种情况)
    2.1 原数组为0的情况,扩容后的大小为minCapacity
    2.2 原数组的1.5倍大于MAX_ARRAY_SIZE的情况,根据minCapacity来判断。
    minCapacity > MAX_ARRAY_SIZE:扩容为Integer.MAX_VALUE
    minCapacity <= MAX_ARRAY_SIZE:扩容为MAX_ARRAY_SIZE
    2.3 其他情况扩容为原数组的1.5倍

五、总结

结合minCapacity的值和三种扩容情况,一共有五种扩容的情况

  1. 当new ArrayList() 第一次add元素时
    minCapacity=Math.max(DEFAULT_CAPACITY, 1)=10,扩容后长度为10
  2. 当new ArrayList(0) 第一次add元素时
    minCapacity=size+1=1,扩容后长度为1
  3. 当数组的1.5倍大于MAX_ARRAY_SIZE且minCapacity小于等于MAX_ARRAY_SIZE时
    新数组扩容到:MAX_ARRAY_SIZE
  4. 当数组的1.5倍和minCapacity(size+1)都大于MAX_ARRAY_SIZE时
    新数组直接扩容到:Integer.MAX_VALUE
  5. 其他情况
    新数组扩容到:原数组的1.5倍
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值