ArrayList的扩容

本文深入解析ArrayList的扩容机制,从两种构造函数和两种add方法入手。默认构造函数分配空数组,有参构造根据指定容量创建。add方法中,当size等于elementData.length时触发扩容。扩容通过grow()函数完成,首次扩容通常为10,之后按1.5倍增长。若扩容后仍小于minCapacity,则直接扩容至minCapacity。
摘要由CSDN通过智能技术生成

前言

ArrayList的扩容会由于ArrayList的构造函数选取的不同有很多种情况,所以我们本文的主要顺序是这样的:先了解ArrayList的构造函数,然后了解ArrayList的两种重载的add方法,以来展示其扩容原理。

ArrayList的两种构造函数

1.默认的无参构造

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

2.有参构造

    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);
        }
    }

另外还有一种有参构造将不在这里介绍了,就是参数是另一种集合的。

两种add方法

1.在尾部的加法

public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

2.在任意位置的加法

public void add(int index, E element) {
        rangeCheckForAdd(index);
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)//注释1
            elementData = grow();//注释2
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }

不难发现第一中加法内部就是调用的第二种加法,接下来我们对第二种加法详细分析一下。
注释1的部分是扩容条件,即当size==elementData.length的时候便扩容,注意:

  1. size 指的是该容器中存放对象的数量;
  2. elementData.length指的是ArrayList底层实现的数组的长度。

接下来我们主要来看看grow()函数的内部:

    private Object[] grow() {
        return grow(size + 1);
    }

grow()的另一种重载,参数为size+1,并且形参命名为minCapacity,我的理解是扩容最小应该满足的容量(因为ArrayList的扩容并不是每次扩容一位的,所以这个是最小的):

    private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 ||//注释3
         elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,//注释4
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {//注释5
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }

注释5的情况比较简单,先介绍注释5吧,
注释5:一般我们在List容器刚刚初始化并且我们使用的是ArrayList的无参构造器来构造ArrayList容器时(这种情况经常发生),还没有使用的时候都是先执行这一步,可见在这一步要满足两个条件:1,原来的底层数组长度为0(没有加入过元素或者全删了);2,使用的是无参构造。
此时我们返回新的底层数组来实现扩容,在此时DEFAULT_CAPACITY=10,minCapacity=1,所以此时底层的数组长度为10。
注释3:当我们使用的不是默认无参构造来初始化或者在这个add操作之前elementData不为空,就进行到下面的注释4,此时进入到ArraysSupport.newLength()函数中。

public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // assert oldLength >= 0
        // assert minGrowth > 0

        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
        if (newLength - MAX_ARRAY_LENGTH <= 0) {
            return newLength;
        }
        return hugeLength(oldLength, minGrowth);
    }

可见,这个函数的返回值就是一个数组,其长度为第一个数组的长度,加上第二个参数与第三个参数中较小的那个。

综述

ArrayList每次扩容为1.5倍。如果按1.5倍扩容后仍不能大于minCapacity=size+1,则扩容到minCapacity,我认为这种情况只会发生在oldElementData长度只有0和1的时候才会发生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值