Let's talk about how ArrayList is dynamically expanded and what kind of mechanism is it?

Posted by ksp on Sun, 06 Feb 2022 21:00:17 +0100

ArrayList source code analysis and overall analysis

ArrayList vs LinkedList

1) Expansion code

For the above two articles, click the details directly. This article mainly analyzes the capacity expansion mechanism and how the ArrayList is expanded?

    /**
     * Default initial capacity.
     * Default initial capacity
     */
    private static final int DEFAULT_CAPACITY = 10;
 private void ensureCapacityInternal(int minCapacity) {
 		//Judge whether the initialized elementData is an empty array, that is, it has no length
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //Math.max gets the maximum value, DEFAULT_CAPACITY=10;
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		
        ensureExplicitCapacity(minCapacity);
    }

 /**
 *Judge whether the current ArrayList needs to be expanded
 **/
 private void ensureExplicitCapacity(int minCapacity) {
        //Fast error reporting mechanism
        modCount++;

        //If the minCapacity is greater than the length of the actual elementData, it means that the length of the elementData array is not enough. If it is not enough, increase the length of the elementData
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

2) minCapacity really understands

When is the real expansion? That must be mincapacity - elementData Length > 0 means that the length of elementData itself is not enough.
The minCapacity size is directly related to the add call. There are two situations:

  1. When add (E) is called, minCapacity=size + 1. When adding for the first time, that is, minCapacity=1, the initial value is determined to be 10, so the final math The minCapacity obtained by Max is also 10;
  2. add(int index, E element) calls the parameterized structure to initialize 'the minimum array capacity required after adding elements' as the actual number of elements after adding elements

3) Growth core expansion logic

//Maximum capacity
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//Increase the capacity to ensure that it can accommodate at least the number of elements specified by the minimum capacity parameter.
//Parameter: minCapacity – minimum capacity required
private void grow(int minCapacity) {
        //Capacity size before assignment
        int oldCapacity = elementData.length;
        //New capacity = 1.5 times the previous capacity
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //Capacity after expansion < previous capacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //Capacity after expansion > maximum capacity
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //Arrays. Capacity expansion strategy of copyof space for time
       
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

Call arrays The copyof method points the elementData array to the contiguous space of newCapacity when the memory space is new
And copy the data of elementData to the new memory space

4) Why is the maximum array size of ArrayList integer MAX_ VALUE-8?

Reference article: Please click here

2 ^ 31 = 2147483648, as an array with 8 bytes storage size

5) Code debugging and verification of expansion logic

We wrote an article about reflection before:
How to get attribute values using reflection
For elementData, we also obtain it through reflection.

The test demo is as follows:

public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		// Define an array and put 21 values into the instruction to see how to dynamically expand the capacity?
		for (int i = 0; i < 21; i++) {
			list.add(i);
			// Without adding an element, the elementData value is obtained through the reflection mechanism and printed
			Class<ArrayList> arrayListClass = ArrayList.class;
			try {
				Field field = arrayListClass.getDeclaredField("elementData");
				field.setAccessible(true);
				Object[] objects = (Object[]) field.get(list);
				System.out.println("The first" + i + "After expansion of elements elementData:  " + objects.length);
			} catch (NoSuchFieldException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

Output result:

elementData after expansion of the 0th element: 10
elementData after the expansion of the first element: 10
elementData after the expansion of the second element: 10
elementData after capacity expansion of the third element: 10
elementData after capacity expansion of the fourth element: 10
elementData after capacity expansion of the fifth element: 10
elementData after capacity expansion of the 6th element: 10
elementData after capacity expansion of the seventh element: 10
The 8th element after the expansion of elementData:
elementData after capacity expansion of the 9th element: 10
elementData after capacity expansion of the 10th element: 15
elementData after capacity expansion of the 11th element: 15
elementData after capacity expansion of the 12th element: 15
elementData after capacity expansion of the 13th element: 15
elementData after capacity expansion of the 14th element: 15
elementData after capacity expansion of the 15th element: 22
elementData after capacity expansion of the 16th element: 22
elementData after capacity expansion of the 17th element: 22
elementData after capacity expansion of the 18th element: 22
elementData after capacity expansion of the 19th element: 22
elementData after capacity expansion of the 20th element: 22

We can clearly see the expansion

  • When the list size is < 10, the capacity is the default value, that is, 10
  • When 10 = < list < 15, the capacity is 1.5 times, that is, 10 * 1.5 = 15
  • When 15 = < list < 21, the capacity is expanded by 15 times, that is, 15 * 1.5 = 22.5, and the integer is 22

Topics: Java