关于一次面试被问到的ArrayList的扩容机制

28 阅读3分钟

先来看一下ArrayList初始化的元素个数

先看ArrayyList的构造方法,先看无参构造函数

    /**
     * Constructs an empty list with an initial capacity of ten.
     *构造一个初始容量为10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

这个是无参数的构造函数,给ArrayList的elementData(元素的数组)进行赋值DEFAULTCAPACITY_EMPTY_ELEMENTDATA(按道理来说这句英文的意思是空数组的意思,但是注释说了是创建容量为10的List,不管了,先看着吧)。

这个是有参数的构造函数

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     *构造具有指定初始容量的空列表。如果initialCapacity为负的话,那么抛出异常
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        //构造具有指定初始容量的空列表。如果initialCapacity为负的话,那么抛出异常
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

再看另一个构造函数

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *按照指定集合的迭代器返回的顺序,构造一个包含指定集合元素的列表。
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
    //使用 toArray 方法将集合 c 转换为数组,并将其赋值给 elementData。elementData 是 ArrayList 内部用于存储元素的数组。
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //判断 elementData 所属的类是否不是 Object[] 类型。
            if (elementData.getClass() != Object[].class)
            //如果 elementData 不是 Object[] 类型,通过 Arrays.copyOf 将其转换为 Object[] 类型。这个步骤的目的是确保 elementData 是                     Object[] 类型的数组。
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
        //如果 elementData 的长度为 0,则执行下面的逻辑。
            // replace with empty array.
            //将 elementData 设置为 ArrayList 内部定义的空数组 EMPTY_ELEMENTDATA。这个数组在 ArrayList 类中是一个静态常量,表示一个空的数组实例。
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

然后来看一下add()方法

public boolean add(E e) {
        //就是ensureCapacitylnternal(size + 1)并且将e添加到size++的位置,我们来看ensureCapacitylnternal(size + 1)方法。
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        //DEFAULTCAPACITY_EMPTY_ELEMENTDATA看着是不是很眼熟,它就是空构造函数赋的值
        //所以这个时候minCapacity其实是为0,所以其实就是DEFAULT_CAPACITY数值为10  
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

然后再看ensureExplicitCapacity(minCapacity);

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //检查当前容量是否小于指定的最小容量(minCapacity)。如果是,说明需要进行扩容。
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

接下来看看grow(minCapacity);方法

private void grow(int minCapacity) {
        // overflow-conscious code
        //获取当前 ArrayList 内部数组的长度,即当前容量。
        int oldCapacity = elementData.length;
        //计算新的容量,通过将当前容量右移一位并加上原始容量,相当于将容量扩大 1.5 倍。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //检查计算得到的新容量是否小于所需的最小容量(即 minCapacity)。如果是,将新容量设置为最小容量。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //检查新容量是否超过了数组的最大容量(MAX_ARRAY_SIZE)。如果是,调用 hugeCapacity 方法来计算新容量,确保它不超过 MAX_ARRAY_SIZE。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //使用 Arrays.copyOf 方法将内部数组 elementData 扩容到新的容量 newCapacity。这个操作会创建一个新的数组,并将旧数组的元素复制到新数组中。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

最后总结一下:无参构造方法调用add之后创建的是10个长度的数组。有参数则直接创建指定长度的数组.。扩容时,小于MAX ARRAY_SIZE = lnteger,MAX VALUE-8 次扩容1.5 倍,超过则直接Integer.MAX VALUE.