ArrayList扩容机制解析

ArrayList构造函数说起:

    //带初始容量参数的构造函数。(用户自己指定容量)
    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);
        }
    }

     //默认构造函数,使用初始容量10构造一个空列表(无参数构造)
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    //构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回,如果指定的集合为null,throws NullPointerException。 
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

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

下面看add方法:

    public boolean add(E e) {
        modCount++;//modCount 记录了 ArrayList 结构性变化的次数,继承自 AbstractList。所有涉及结构变化的方法都会增加该值。
        /*elementData是 ArrayList 的数据域,被 transient 修饰,序列化时会调用 writeObject 写入流,反序列化时调用 readObject 重新赋值到新对象的 elementData。*/
        /*size 是当前实际大小,elementData 大小大于等于 size。*/
        add(e, elementData, size);
        return true;
    }

    private void add(E e, Object[] elementData, int s) {
         //如果实际大小与数据域大小相等,则扩容(grow方法)
        if (s == elementData.length)
            elementData = grow();
        //否则直接添加,这里看到ArrayList添加元素的实质就相当于为数组赋值
        elementData[s] = e;
        size = s + 1;
    }

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

    //扩容方法
    private Object[] grow(int minCapacity) {
        //Arrays.copyOf:以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。 
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

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

    private int newCapacity(int minCapacity) {
        //oldCapacity为旧容量,newCapacity为新容量
        int oldCapacity = elementData.length;
        //将oldCapacity 右移一位,其效果相当于oldCapacity /2,我们知道位     运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然后检查新容量是否大于最小需要容量
        //当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。
        if (newCapacity - minCapacity <= 0) {
            //如果当前数据域等于默认容量
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                 //获取默认的容量和传入参数的较大值
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) 
                throw new OutOfMemoryError();
             //得到最小扩容量
            return minCapacity;
        }
        //如果新容量小于要分配的最大数组大小,则为新容量,否则执行hugeCapacity方法
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
        //如果新容量大于要分配的最大数组大小,则新容量为Integer.MAX_VALUE,否则为要分配的最大数组大小
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }

流程图:
在这里插入图片描述
总结:
扩容之后,如果新容量大小小于最小需要容量,需比较当前数据域是否等于默认容量,如果相等,则获取默认的容量和最小需要容量的较大值,否则获取最小需要容量;如果新容量大小大于最小需要容量,如果新容量小于要分配的最大数组大小,则为新容量,否则执行hugeCapacity方法,hugeCapacity方法:如果新容量大于要分配的最大数组大小,则新容量为Integer.MAX_VALUE,否则为要分配的最大数组大小。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页