ArryList underlying data structure and common method source code understanding

Posted by lailaigogo on Fri, 04 Mar 2022 11:33:55 +0100

ArryList underlying data structure and common method source code understanding

1. First look at a test code

package com.heyuanhang.conllention_;

import java.util.ArrayList;

/**
 * @Author He Yuanhang
 * @Date: 2021/4/17 12:57
 * @Version 1.8
 */
public class ArrayListYuanMa {
    public static void main(String[] args){
        //The parameterless constructor initializes an empty array when defining an ArrayList object
        //When adding data elements for the first time, an array of 10 lengths will be applied
        ArrayList list=new ArrayList();
        list.add("heyuanhang");
    }
}

(1) View the parameterless construction of ArrayList

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

Note: the above default capability_ EMPTY_ Elementdata is an attribute of ArryList. Obviously, this attribute is an empty array of Object type.

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

You can see from here that when the parameterless construction of ArryList is executed, the elementData array maintained at the bottom of ArryList is initialized to an empty array of {}.

(2) OK, now there is an empty array, but its length is. Now execute the add method to add elements

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

Ensure capacity internal (size + 1) here; What's the method? You might as well enter:

 private void ensureCapacityInternal(int minCapacity) {
 
        //Let's judge whether elementData is {}
        
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

From this, we can know that the original method is to judge whether the length of elementData is 0. If it is 0, assign 10 to minCapacity, and expand the capacity by using the ensureExplicitCapacity(minCapacity) method.

(3) Enter the ensureExplicitCapacity(minCapacity) method. The source code is as follows:

private void ensureExplicitCapacity(int minCapacity) {
 
        //Record the number of times this array has been modified
        
        modCount++;

        // overflow-conscious code
        //See if the length of the array after adding an element exceeds the existing array elements
        //Capacity expansion starts as soon as possible
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

Note: the initial value of minCapacity parameter above is size+1, which is the value after adding an element

(4) Enter the real capacity expansion method grow th method:

private void grow(int minCapacity) {
        // overflow-conscious code
        /*
        Gets the length of the existing array
        */
        int oldCapacity = elementData.length;
        
        //Assign 1.5 times of the existing length to newCapacity
        
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //If the length is still less than minCapacity after 1.5x capacity expansion
        //Assign minCapacity to newCapacity for capacity expansion
        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);
    }

As can be seen from the above code, the capacity of the original ArryList is insufficient, and then it will be expanded by 1.5 times.

(5) So far, ArryList has been expanded. When adding elements for the first time, the expansion size is 10

(6) The last step is to store the data. elementData[size++] = e;

From this, we can draw a conclusion:

When the ArryList nonparametric structure is used, the space of 10 sizes will be allocated when adding elements for the first time. When the capacity is insufficient, the capacity will be expanded by 1.5 times.

It is necessary to take a look at the parametric structure of ArryList:

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

(it can be seen from the above code that when the transmitted capacity is greater than 0, the space of the specified size will be allocated directly. If it is equal to 0, the function is equivalent to parameterless construction, otherwise an exception will be thrown.)

2, Add the element add(int index,Object obj) to the specified location

Test code:

package com.heyuanhang.conllention_;

import java.util.ArrayList;

/**
 * @Author He Yuanhang
 * @Date: 2021/4/17 12:57
 * @Version 1.8
 */
public class ArrayListYuanMa {
    public static void main(String[] args){
        ArrayList list = new ArrayList(8);
        list.add(0, 100);
        System.out.println(list.toString());
    }
}

The parameter structure is adopted, and the specified expansion length is 8. Similarly, when it reaches 8, it will still be expanded by 1.5 times.

(it should be noted here that the insertion position index of the specified position is not arbitrary, but has certain specifications. It cannot be considered as long as it is within the size of the applied array. This is wrong. It must be clear that the value of index must be less than the length occupied by existing elements, just like when 8 sizes are applied but data has not been added The value of the effective data length size of the face is 0, so an exception will be thrown when the index is greater than or less than 0)

(1) Now enter an overloaded method add(int index,Object obj) of add

public void add(int index, E element) {
       //This method is to judge whether the position of index is reasonable
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

(2) Execute rangeCheckForAdd(index);

private void rangeCheckForAdd(int index) {

        //If the effective length is exceeded
        //Or less than 0
        //Will throw an exception
        
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

(3) After checking the validity of the index, enter the ensureCapacityInternal(size + 1) method to judge whether the elementData is an empty array {}. The function of this method has been mentioned above, so I won't talk about it here.

(4) Then enter ensureExplicitCapacity(minCapacity); Method to confirm whether capacity expansion is needed.
(5) Finally, the data is stored.

3, Delete the element remove(int index) at the specified location

Test source code:

package com.heyuanhang.conllention_;

import java.util.ArrayList;

/**
 * @Author He Yuanhang
 * @Date: 2021/4/17 12:57
 * @Version 1.8
 */
public class ArrayListYuanMa {
    public static void main(String[] args){
        ArrayList list = new ArrayList(1);
        list.add(100);
        list.add(100);
        list.add(100);
        list.add(100);
        list.remove(0);
        System.out.println(list.toString());
    }
}

(1) Enter the remove method

 public E remove(int index) {
        //Judge whether the index is legal
        rangeCheck(index);
        //Record the number of times the array has been modified
        modCount++;
        //Extract the element at the position to be deleted
        E oldValue = elementData(index);
         //The value of numMoved here is actually the number of elements after the index position
         
         //For example, if the effective length of elementData is 3
         
         //When you want to delete the element in the second position, that is, index=1, there are still 3-1-1 = 1 elements after position 1
        int numMoved = size - index - 1;
        if (numMoved > 0)
        //Make array copy
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

For the system in the above code arraycopy(elementData, index+1, elementData, index, numMoved); Description of method parameters

elementData :Source array
index+1:Starting position of element group
 elementData:target array
 index: Start position of target array
 numMoved: The length of the element to be assigned

(2) OK, here you go to rangeCheck(index); Methods:

private void rangeCheck(int index) {
        
        //If the valid length is exceeded, an exception is thrown
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

(3) Enter the E elementData(int index) method

E elementData(int index) {
         //Returns the element at the specified location
        return (E) elementData[index];
    }

(4) OK, that's it. The deletion method is finished. This method returns the deleted element value.

4, The Boolean addall (collection <? Extensions E > C) method that adds a collection to the current collection

Test code:
Add the elements from the list1 collection to the list collection

package com.heyuanhang.conllention_;

import java.util.ArrayList;

/**
 * @Author He Yuanhang
 * @Date: 2021/4/17 12:57
 * @Version 1.8
 */
public class ArrayListYuanMa {
    public static void main(String[] args){
        ArrayList list = new ArrayList();
        ArrayList list1 = new ArrayList();
        list.add(100);
        list.add(100);
        list.add(100);
        list.add(100);
        list1.add("He Yuanhang");
        list1.add("heyuanhang");
        list.addAll(list1);
        System.out.println(list.toString());
    }
}

(1) Enter the Boolean addall (collection <? Extensions E > C) method:

 public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

A brief overview of the functions of the above methods:

1. First convert the passed collection into an array of Object type

2. Then get the length of the array

3., we use the ensureCapacityInternal method to determine whether the current elementData is {}. Then we call the void ensureExplicitCapacity(int minCapacity) method to determine whether we need to call grow for expansion.
4. Just copy the array again.

This is the understanding of the underlying data structure of ArryList and the source code of common methods.

Topics: Java data structure arraylist