ArrayList扩容机制

与扩容有关的成员变量:

    // 数组默认容量
    private static final int DEFAULT_CAPACITY = 10;

    // 空数组,代表new ArrayList(0)
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 默认空数组,代表 new ArrayList();
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 存储元素的数组
    transient Object[] elementData;

    // 当前数组包含的元素个数
    private int size;
    
    // 数组最大元素个数
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList构造方法:

    // 无参构造:对应默认空数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    // 有参构造:根据参数创建相应大小的数组
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { // 创建大小为initialCapacity的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { // 大小为0, 用空数组替代
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    // 有参构造:根据参数创建相应大小的数组
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { // 创建大小为initialCapacity的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { // 大小为0, 用空数组替代
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

扩容的起点:从add(E e)方法开始

    public boolean add(E e) {
        // size + 1 表示扩容后数组需要的最小容量,此时size = 0
        ensureCapacityInternal(size + 1);
        // 扩容后在对应位置赋值,元素个数+1
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        // calculateCapacity方法用于计算真正的最小容量(下面再解释)
        // ensureExplicitCapacity方法判断是否需要扩容
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 如果是用无参构造得到的数组 返回默认容量大小,也就是10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 否则容量不变,还是 size + 1
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        // 修改次数
        modCount++;
        // 最小容量大于当前数组长度才需要扩容
        if (minCapacity - elementData.length > 0)
            // 扩容的核心方法
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // 旧容量,也就是当前的数组长度
        int oldCapacity = elementData.length;
        // 扩容后的新容量,约等于旧容量的1.5倍,因为有旧容量可能是奇数
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 扩容后的新容量还是小于最小容量,则还是使用最小容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 新容量超过默认的最大数组容量,新容量 = Integer.MAX_VALUE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 复制当前所有元素到扩容后的新数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

根据整个扩容流程可以得出扩容分为如下三种情况:

  1. 如果使用无参构造方法创建ArrayList:添加第一个元素的时候数组长度扩容到10。之后直到添加第11个元素时开始正常扩容(原容量的1.5倍)
  2. 如果使用有参构造方法创建ArrayList,且参数为0:添加第一个元素的时候则开始正常扩容。
  3. 如果使用有参构造方法创建ArrayList,且参数大于0:直到数组满后,再添加元素时则开始正常扩容

补充:grow()中为什么是if (newCapacity - minCapacity < 0)而不是if (newCapacity < minCapacity)?

假设是 if (newCapacity < minCapacity),该语句为true时,是在数组容量非常小(0,1,2)的情况下,扩容后还没有当前容量大,因此还不如直接使用最小容量(当前容量+1)作为扩容后的容量。但是如果此时数组容量处于临界值,1.5倍扩容后溢出变成了一个大负数,该语句还是为true,因此还是把最小容量最为扩容后的新容量,那么由于这时元素数量已经很多了,再把所有元素复制到新数组上效率就比较低。

假设是if (newCapacity - minCapacity < 0),还是在数组容量处于临界值的情况下,这时就是扩容后溢出得到的大负数 + (-最小容量),此时(-最小容量)也是个大负数。两数相加再次溢出得到个正数,因此不会执行newCapacity = minCapacity。这是扩容后的容量则由hugeCapacity()方法得到:如果最小容量大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),新容量则为Integer.MAX_VALUE,否则为MAX_ARRAY_SIZE。这就避免了临界值之后的每次扩容变成当前容量+1而造成的复制元素所带来的巨大开销,而是直接扩容为MAX_ARRAY_SIZE。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值