ArrayList underlying source code

Posted by Moron on Sat, 26 Feb 2022 09:02:57 +0100

ArrayList underlying source code

conclusion

  1. ArrayList maintains an array of object type elementdata (transient Object[] elementData;)
  2. When creating an ArrayList object, if a parameterless constructor is used, the initial elementData capacity is 0. If it is added for the first time, the expanded elementData is 10. If it needs to be expanded again, the expanded elementData is 1.5 times.
  3. If the constructor with the specified size is used, initialize the elementData capacity to the specified size. If capacity expansion is required, directly expand the elementData to 1.5 times.

The transient keyword indicates instantaneous or transient. The attribute or member modified by transient indicates that the attribute will not be serialized

Data structure of ArrayList

The underlying data structure of ArrayList is an array of type Object

Inheritance system


Based on array implementation, it supports dynamic capacity expansion, facilitates element insertion and deletion, and supports random access.
ArrayList inherits from AbstractList and implements Cloneable, List, RandomAccess and Serializable interfaces.

  • Inherits AbstractList and implements List. It is an array queue, which provides related functions such as adding, deleting, modifying and traversing.
  • The clonable interface is implemented, which overrides the function clone(), so it can be cloned.
  • RandmoAccess interface is implemented to realize the effect of random access.
  • The Serializable interface is implemented, which means that ArrayList supports serialization

Member variable

    //The default capacity size is 10
    private static final int DEFAULT_CAPACITY = 10;
    //Empty array. Use new ArrayList(0) when the incoming capacity is 0
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // Empty array with capacity of 0 after creation
    // Adding an element initializes the array size to 10
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //The bottom layer is realized by array to store the actual data
    //The identity transient indicates that it is not serializable
    transient Object[] elementData; 
    //The number of elements in the collection, not the capacity
    private int size;
    // Maximum array capacity
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Construction method

1. Construction method with int type

//Pass in the initial capacity. If the capacity is greater than 0, the array with the corresponding size of elementData will be initialized
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            //Arrays are created immediately, not lazily
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //If the capacity passed in is 0, the default empty array empty is used_ ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //An exception is thrown when the capacity is less than 0
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
  • If the parameter passed in is greater than 0, an array with initial capacity will be created
  • If the passed in parameter is equal to 0, the default empty array is used
  • If the parameter passed in is less than 0, an exception is thrown

2. Nonparametric construction method

//The default initial capacity will not be passed in. If the null parameter constructor is used, the default default default capability will be used_ EMPTY_ The elementData array is assigned to elementData
    //It will be expanded to the default size of 10 only when the first element is added
    public ArrayList() {
        // An empty array was created
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

3,Collection<? Extensions E > type construction method

public ArrayList(Collection<? extends E> c) {
	// Convert the set in the parameter into an array and assign it to elementData 
    elementData = c.toArray();
    // If the size of the array is not equal to 0
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // If the array type returned by c.toArray() is not of type Object []
        // Then use arrays Copyof() creates an array with size and type of Object [] and copies the elements in elementData into the new array
        // Finally, let elementData point to the new array
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else { // Otherwise, set the element array to an empty array
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
  1. Convert the set in the parameter into an array and assign it to elementData;
  2. Whether the parameter set is empty. By comparing the size with the size of the array length in the first step.
  3. If the parameter set is empty, set the element array to be empty, that is, EMPTY_ELEMENTDATA is assigned to elementData;
  4. If the parameter set is not empty, then judge whether the parameter set is successfully converted to an array of Object type. If the conversion to an array of Object type is successful, copy the array and convert it to an array of Object type.

Capacity expansion mechanism

The expansion of ArrayList mainly occurs when adding elements to the ArrayList collection. From the analysis of the add() method, we can see that before adding, we must ensure that the capacity of the collection can put down the added elements. It mainly goes through the following stages:

First, the ensureCapacityInternal (size+1) method is invoked in the add() method to determine the value of the minimum set volume minCapacity for the set to ensure the success of the added element. The parameter is size+1, which means the actual number of elements in the collection after adding elements to the collection successfully. In other words, in order to ensure the success of adding elements, the minimum capacity mincapacity of the collection should be size+1. In the ensurecapacityinternal method, first judge whether elementData is the default empty array. If so, mincapacity is the larger of mincapacity and the default capacity of the collection.

Second, call the ensureExplicitCapacity(minCapacity) method to determine whether the collection needs to expand the existing element array in order to ensure the success of adding elements. First, add one to the structural modification counter; Then judge the size of minCapacity and the length of the current element array. If minCapacity is larger than the length of the current element array, it needs to be expanded to enter the third stage.

Third, if you need to expand the existing element array, call the grow(minCapacity) method. The parameter minCapacity represents the minimum capacity of the collection to ensure the success of adding elements. During capacity expansion, first increase the length of the original element array by 1.5 times (oldcapacity + (oldcapacity > > 1)), and then compare the capacity after capacity expansion with minCapacity: ① if the new capacity is less than minCapacity, set the new capacity to minCapacity; ② If the new capacity is greater than minCapacity, specify the new capacity. Finally, copy the old array to the new array after capacity expansion.

1, calling the ensureCapacityInternal method in the add() method, passing in the number of current collection elements, indicating the minimum capacity needed to add elements.

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Judge whether the capacity is sufficient. The number of elements in the collection is passed in
        elementData[size++] = e;
        return true;
    }

2. In the ensureCapacityInternal method, first call the calculateCapacity method to determine whether it is an empty array. If it is empty, the initial capacity is 10

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  //Determine whether to expand capacity
    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {   //Determine whether it is an empty array
            return Math.max(DEFAULT_CAPACITY, minCapacity); //Is an empty array, with a minimum capacity of 10
        }
        return minCapacity;
    }

3. Call the ensureExplicitCapacity(int minCapacity) method in the ensureCapacityInternal() method to judge whether the current array needs to be expanded when adding elements to the segment collection.

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;   // Record the number of times the collection has been modified to prevent exceptions in multi-threaded operations
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)  // If the capacity of elementData is insufficient, it needs to be expanded
            grow(minCapacity); //Capacity expansion
    }

4. Call the grow(int minCapacity) method in the ensureExplicitCapacity() method to expand the capacity. Finally, call Arrays.. The copyof () method is used to copy the original array and assign elementData to the new array.

private void grow(int minCapacity) {
    // overflow-conscious code
    // Original capacity
    int oldCapacity = elementData.length;
    // The new capacity is 1.5 times the original capacity
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // If the new capacity is less than the specified capacity
    //If the new capacity is 0 for the first time, you need to give the minimum capacity to the new capacity first
    if (newCapacity - minCapacity < 0)
    	// Assign the specified capacity to the new capacity
        newCapacity = minCapacity;
    // If the new capacity is greater than the specified capacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    	// Specify the new array capacity
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    // Copy the old array to the new array after capacity expansion
    elementData = Arrays.copyOf(elementData, newCapacity);
}

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

Topics: Java