ArrayList source code analysis

Posted by Judas on Sat, 16 Oct 2021 10:35:35 +0200

10.15 analysis of ArrayList collection source code

The code used for the test is as follows

public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        list.add(20);
        list.add(30);
        list.add(40);

    }

First, create an ArrayList set. Here I use the nonparametric structure. The parametric structure is discussed below

Through deBug, we enter the collection and find that it is a parameterless construction of the called collection

//The parameterless construction source code of ArrayList,
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

elementData and defaultcapability_ EMPTY_ elementData are all attributes in the ArrayList collection. There is a modifier transient in front of elementData, which is used to place the attributes to be serialized

elementData is the array used to store data at the bottom of ArrayList. When ArrayList is created in JDK8, an empty array is created by default. The array will be expanded only when data is added for the first time

transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

Next, execute the add() method

list.add(i);

//First of all, because we store the basic data type and the collection needs to store the Object, java performs automatic boxing.
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Then continue to debug

//This is the method of adding elements to ArrayList
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

We explain this method bit by bit

ensureCapacityInternal(size + 1);//1

//First, we use size+1 because we have to judge whether there is space for the array before adding it
//Next, we enter the method through a breakpoint
private void ensureCapacityInternal(int minCapacity)//minCapacity = 1 {
    
    	//The function of this method is to determine the capacity of the array
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

Step 1: analyze calculateCapacity

//Enter the calculateCapacity(elementData, minCapacity) method
//The function of this method is to judge whether the array is empty and determine the capacity of the array
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    
    //First, judge whether the array is an empty array. Since we added it for the first time, the array is an empty array, so it will enter the if judgment
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            
            //private static final int DEFAULT_CAPACITY = 10; 
            //I copied this from the top of the ArrayList. It is easy to find that this is a java defined ArrayList. The default capacity is 10
            //The maximum value of the default capacity and the minimum required capacity is returned here. Obviously, the maximum value is the default capacity of 10, which is why the default capacity of ArrayList is 10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
    //If it is not empty, that is, it is not the first time to add, the minimum required capacity is returned
        return minCapacity;
    }

Step 2: analyze the ensureExplicitCapacity() method

private void ensureExplicitCapacity(int minCapacity) {
    	//This data is used to determine how many times the set has been modified
        modCount++;

        // overflow-conscious code
    	//Judge whether the minimum capacity is greater than the array length. If it is greater than, expand the capacity. Otherwise, do not expand the capacity
    	//The minimum capacity of our minCapacity is 10, which is greater than the length of the array 0
        if (minCapacity - elementData.length > 0)
            //This is the final expansion method
            grow(minCapacity);
    }

Step 3 locate the growth (mincapacity) expansion method

//Capacity expansion method of ArrayList
private void grow(int minCapacity) {
        // overflow-conscious code
    	//Assign the length of the array to the old capacity
        int oldCapacity = elementData.length;
    
    	//This is the reason for the 1.5x expansion of ArrayList
    	//Oldcapacity > > 1 is equivalent to dividing by 2, so the new capacity is the old capacity + old capacity / 2, that is, 1.5 times the expansion capacity
    	//Since the length of the element array stored for the first time is 0, the old capacity is expanded by 1.5 times or 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    	//The next judgment is to prevent the first time when the capacity is 0, it is still 0 after capacity expansion
    	//if will compare the expanded capacity with the minimum capacity. When the expanded capacity is still less than the minimum capacity, the minimum capacity will be assigned to the new capacity,
    	//I think the purpose of this step is to ensure that this element can be added to the collection after capacity expansion.
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
    
    	//This one is to judge whether the capacity of the set exceeds the maximum value of Integer
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
    
        // minCapacity is usually close to size, so this is a win:
    	//Call Arrays.copyOf() to expand the array
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

The fourth step is to store the elements in the collection

//Put the element in position 0 and set size+1 
elementData[size++] = e;

Step 5: insert and return results

return true;

Summary:

To sum up, the process from creating ArrayList to adding data for the first time is completed

  1. Create an ArrayList object, call the parameterless construction, and assign the default empty array to the stored data elementData
  2. Before adding an element, you will first judge whether it is empty. If it is empty, the maximum value will be taken from the minimum capacity (size+1) and the default capacity (10). If it is not empty, the minimum capacity will be returned directly
  3. Next, judge whether to expand the capacity, and compare the minimum capacity with the array length
  4. If you need to expand the capacity, you will first assign the length of the array to the old capacity, then expand the old capacity by 1.5 times and assign it to the new capacity, and then compare the new capacity with the minimum capacity in the following key step. If it is less than, you will assign the minimum capacity to the new capacity, and then complete the expansion through the Arrays.copyOf() method
  5. If you do not need to expand the capacity, you can add it directly, change the size + +, and finally return true.

Next, the additions in the for loop are basically the same. Let's continue to explore the additions below the loop

list.add(20);

Go inside the method, and then let us more intuitively see the changes of data through pictures

The elements in the collection are as follows

Judge that the set is not empty

Determine whether capacity expansion is required

Capacity expansion

Collection after capacity expansion

Add element

Through the above process, it is easy to know that ArrayList will judge each time to ensure that each data can be successfully added

Finally, let's look at the parametric structure of ArrayList

 public ArrayList(int initialCapacity) {
     	//If the value passed in is greater than 0, an array with the size of initialCapacity is created
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0)//If the value passed in is 0, the empty array will be assigned in the past{
            //private static final Object[] EMPTY_ELEMENTDATA = {};
          
            this.elementData = EMPTY_ELEMENTDATA;
        } else//If the value passed in is less than 0, an exception will be thrown{
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

summary

The blog is not perfect. If you encounter any mistakes in the article, I hope you can point out in the comment area that you have made progress and created constantly. Give me a praise!

Topics: Java set arraylist