ArrayList扩容机制

ArrayList继承了AbstractList,实现了List接口,底层实现基于数组,因此可以认为是一个可变长度的数组。

ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去

ArrayList 部分源码

 /**
     * 默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 定义一个空数组以供使用
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 也是一个空数组,构造无参构造方法时用到
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存放数组中的元素,transient修饰,不被序列化
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 表示数组中所包含实际元素的个数,和elementData.length不同
     */
    private int size;

    /**
     * 记录list被修改的次数
     */
    protected transient int modCount = 0;

    /**
     * 分配数组的最大大小,(有些虚拟机在数组中保留一些头词,如果 分配更大的数组可能导致 OutOfMemoryError:请求的数组大小超过虚拟机限制 )
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 无参构造函数,返回空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 有参构造函数,构造一个具有指定初始容量的空list,initialCapacity = 0 时 ,返回空数组EMPTY_ELEMENTDATA,与无参构造返回的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 不同
     * @param initialCapacity
     */
    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);
        }
    }

    /**
     * 有参构造函数,构造包含指定元素的list
     * @param c
     */
    public ArrayList(Collection<? extends E> c) {
        //将传入的集合转为数组
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray可能(不正确地)不返回Object[]   (see 6260652)
            if (elementData.getClass() != Object[].class)
                //通过Arrays.copyOf方法把集合中的元素拷贝到elementData中
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

    /**
     * 将指定元素追加到列表末尾
     * @param e
     * @return
     */
    public boolean add(E e) {
        int i =0;

        //size + 1  把数组实际长度加1,以确保能保持下下一个数据
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     * 将指定元素插入到指定的位置
     * 将当前元素移动到该位置(如果有的话),并 移动右边的后续元素(在它们的下标上加1)
     * @param index
     * @param element
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * minCapacity = size + 1
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /**
     * 计算容器大小
     * 如果是调用了无参构造返回的空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),
     * 则返回 默认初始容量(DEFAULT_CAPACITY=10) 和 minCapacity = size+1  中的最大值 ,由于第一次调用 size=0,故此时返回 10;
     * 否则返回 minCapacity = size+1,注意只有第一次调用add时,才返回 1,此后的调用需要根据实际的数组长度size+1
     * @return
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    /**
     * 判断是否需要调用扩容方法
     * 1. 如果 new ArrayList 时 给定了长度(长度 > 0),即调用有参构造 ,那elementData.length 就是 传入的长度,
     *    而第一次调用add 时,由于size=0, 所以 minCapacity = size +1 = 1, 故 不需要调用 扩容函数
     * 2. 如果 new ArrayList 时 没有给定了长度,即调用无参构造,返回的空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,
     *    那 elementData.length = 0 , 则满足条件,需要调用扩容方法
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //如果minCapacity > elementData.length ,即 当前数组实际长度 大于 数组容量时 ,调用扩容方法 grow()
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * 扩容方法
     * @param minCapacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; //原来的容量
        // 新容量 = 原容量右移一位+原容量 = 原容量*1.5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //若新容量 < 数组实际长度+1 , 则 直接让 数据实际长度+1 为新容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果新容量 大于 分配数组的最大大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 通过Arrays.copyOf方法把原数组的内容放到更大容量的数组里面
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    //如果数据实际长度+1 大于 分配数组的最大大小, 则返回 最大整数,否则 返回  分配数组的最大大小
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

假如我们放入11个值,那ArrayList的扩容过程是怎样?

ArrayList 初始化 elementData 数组长度为0,放入第一个值的时候,elementData 是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以第一次扩容是DEFAULT_CAPACITY,也就是10,所以当前elementData数组长度是10, 然后放入11个值,所需数组长度是12, 大于elementData 数组长度10,需要扩容,第二次扩容是按当前数组长度的一半,也就是5,所以扩容长度是15,15比所需长度12大,满足条件进行扩容,所以elementData 数组长度是15

总结:
调用add()方法 时 扩容步骤:

  1. 如果 放入的值个数小于10个,那 数组容量就是10;
  2. 如果 放入的值个数 大于10个 ,小于 15 个 ,则 容量为 10*1.5 = 15 ;
  3. 如果 放入的值 个数大于15 个,小于 22 个,则容量为 15*1.5 = 22
  4. ·····以此类推,每次递增到 原数组容量的1.5倍
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值