ArrayList capacity expansion mechanism

Posted by ev5unleash on Tue, 22 Feb 2022 15:10:17 +0100

Source code analysis of ArrayList capacity expansion mechanism

First, correct a problem here. I found that when many blogs introduce the parameterless construction of ArrayList calls, the initial capacity is 10. In fact, there is a problem here. The parameterless construction is actually called, and the initial capacity is 0. Look at the source code

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

This code is very simple and easy to understand, that is, it will be defaultcapability_ EMPTY_ elementData is assigned to elementData. So what are these two?

    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

elementData is an array of ArrayList data. DEFAULTCAPACITY_EMPTY_ELEMENTDATA is an empty array dedicated to initialization. Therefore, when new ArrayList(), the capacity of ArrayList is 0.
Next, let's look at the capacity expansion. What does the ArrayList instance object do when it calls the add() method? Look at the source code

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

Many people think that the add method is to add elements to the array. In fact, the first step here is not to add elements, but to call the ensureCapacityInternal method to expand the capacity. The size variable here is the actual capacity of the elementData array. Keep looking down.

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

The ensureExplicitCapacity method is invoked in the ensureCapacityInternal method, and the calculateCapacity method is invoked in the ensureExplicitCapacity method. Let's take a look at the calculatecapacity method first.

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

In the calculateCapacity method, we first make an if judgment, which is made for parameterless construction. The following is because no construction will be defaultcapability_ EMPTY_ elementData is assigned to elementData, so it is naturally true here. At this time, default will be set_ The capacity variable is compared with the passed in minCapacity variable and returns the maximum value. DEFAULT_CAPACITY is the user-defined default initial capacity, and minCapacity is the size+1 just now. If the return of if is false, minCapacity is returned. The significance of summarizing this code is to see whether it is an object constructed by new without parameters. Keep looking down

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

In the ensureExplicitCapacity method, it specifically determines whether capacity expansion is needed. modCount + + is not important for capacity expansion, so it is not introduced here.
The following is an if judgment, which is also easy to understand. It is to compare the capacity needed today with the original capacity. If the capacity needed today is small, call the grow th method, which is the method to actually expand the capacity.

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        //1
        int oldCapacity = elementData.length;
        //2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //3
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //4
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //5
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

The first code is to save the original capacity into the oldCapacity variable.
The second code is to save the current new capacity into the newCapacity variable. Oldcapacity + (oldcapacity > > 1), which is what we all call the magic code of capacity expansion of 1.5 times. Ha ha, actually, there is nothing magical, that is, move the original capacity to the right by 1 bit, that is / 2. Then add the original capacity.
The third code is an if statement, which is also done for the initial call of parameterless construction. Because the right shift of 0 is plus 0 or 0, newCapacity and minCapacity are compared here. if minCapacity is large, it means that the add method is called for the first time after the initialization of parameterless structure. Secondly, the third construction method is described below, and this construction will not be explained here.
In the fourth code, this self understanding defines the boundary, MAX_ARRAY_SIZE should mean a large boundary value of jdk. Compare it with newCapacity to see if it exceeds this value.
The fifth code, through arrays The copyof method copies the array with the new capacity.


It is hereby declared that the jdk version of this article is 1.8.

Topics: Java