Automatic capacity expansion principle of ArrayList

Posted by josephman1988 on Tue, 04 Jan 2022 04:44:16 +0100

preface

The underlying data structure of ArrayList is array,

  • Array is characterized by fast query and slow addition and deletion
    The reasons are: sequential storage and index. You can directly locate the elements according to the index, so the query is fast; Because it is stored sequentially, adding or deleting will affect subsequent elements.

1. Start by creating an ArrayList set as follows:

       ArrayList<String> list = new ArrayList();

2. After entering the source code, you can see the following source code:

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    transient Object[] elementData; // non-private to simplify nested class access
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  1. First, there is an assignment code in the construction method of ArrayList. To the left of the equals sign is an Object type constant array customized by the source code, and then assigned to an Object type elementData array modified with transient.
  2. The above conclusion is that when creating objects, an array is created at the bottom of ArrayList, and the initial capacity of the array is empty.

First add element in ArrayList collection:

1. When you call the add method for the first time, you need to enter the source code.

       list.add("aa");

2. The source code of the add method is as follows:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  1. When the add method is called, a string "aa" is passed in.

  2. The first sentence in the body of the add method calls the ensureCapacityInternal method. In Chinese, it means: (determine the internal capacity) the minimum capacity is 1. Because size is a variable of type int customized by the source code and has no initial value, size is 0.

  3. The second sentence is size + + in the empty array. At this time, the elementData array is 0. Assign the passed value to the 0 subscript of the array.

  4. Enter the (determine internal capacity) method, and the minimum capacity is 1.

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

5. In the method of determining internal capacity, another method (calculating capacity) is called, which passes two parameters: one is an empty array and the other is the minimum capacity. Then catch up with the source code.

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

6. At this time, a judgment is made in the method, and the condition is true, because this statement is the one called by the bottom layer when creating the collection object.
7. Call a method of Math class to compare the maximum value. The minimum capacity is 1 and default_ The capability constant is 10, because the source code defines and assigns the initial value to 10.

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

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

8. If the condition that 10 minus 0 is greater than 0 is true, call the grow th method to pass in the minimum capacity of 10.

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

9. At this time, minCapacity is 10. The second and last sentences of the source code are the core.
newCapacity = minCapacity; After the old capacity is assigned to the new capacity, the new capacity is now 10
The last sentence is to copy the new capacity 10 to elementData through the copyof method in the Arrays tool class and assign it to elementData. The elementData to the left of the equal sign points to the original empty array. The new array length becomes 10.

Conclusion: when the add method is called for the first time, the array length is 10


The ArrayList collection calls the add method for the second time to add elements:

       list.add("bb");
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  1. The length of the array is 10, aa has been stored in the 0 subscript, and size + + has now become 1. The minimum capacity is now 2
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }


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

    //The meaning of this code is whether to determine the expansion. if the condition is true, the expansion is executed.
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  1. At this time, elementData is 10 and minCapacity is 2
  2. If the condition does not hold, the grow method will not be called. When the 11th data is added, the growth capacity expansion method will be called for capacity expansion.

Call the capacity expansion method when minCapacity is 11: the capacity expansion is 1.5 times the original.

The expansion code is selected from the box above.

Final summary:

  1. The underlying data structure of ArrayList is array. When creating an ArrayList Object, the underlying initializes an empty array. The array is of Object type and the array name is elementData. When the element is added for the first time, the length of the array is expanded to 10.

  2. When you add it for the 11th time, the capacity expansion mechanism will be triggered. In fact, you call the grow method, and the capacity expansion is 1.5 times the length of the original array.

  3. During each expansion, a new array is created, and the elements of the old array are copied to the new array through the Arrays tool class. elementData points to the new array

  4. ***Instead of splicing on the basis of the old array, the capacity expansion creates a new array 1.5 times the length, and copies the elements of the old array to the new array.

Topics: Java Container