Java source code analysis (3) ArrayList

    ArrayList is a collection class that we often use, so in this article we will learn the source code of ArrayList together.

1. Create an ArrayList

    First, we start by creating an ArrayList. The following code creates an empty ArrayList:

List<String> list = new ArrayList<>();

    Look at the source code of the constructor:

    The code comment says: Build an empty List with a capacity of 10. List is implemented based on arrays. As you can see, the constructor creates an empty array here. Huh? I didn't see the capacity is 10, we will introduce this later.

Two, add

1. Add elements

    After creating an empty ArrayList, we call the add method to add elements to it, as follows:

list.add("a");
list.add("b");
list.add("c");

    Look at the source code of the add method:

    The core is to call the add method in the red box, see its implementation:

    It can be seen that it is judged whether the size has reached the length of the array, and if it has reached the length, call the grow() method to expand the capacity, then assign the element to the position of size, and add 1 to size. Brief summary:

   1 expansion 2 assignment 3 plus one.

2. Expansion principle

    Next, let's see how the grow function expands:

    Copy the new capacity array based on the current array and new capacity (size + 1), see how the newCapacity function determines the capacity of the new array:

    Code Commentator Flip: Returns a capacity at least as large as the given minimum capacity. Returns the current capacity increased by 50% if sufficient. A capacity larger than MAX_ARRAY_SIZE will not be returned unless the given minimum capacity is greater than MAX_ARRAY_SIZE. In the expansion operation, several constants are used:

private static final int DEFAULT_CAPACITY = 10;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
@Native public static final int   MAX_VALUE = 0x7fffffff;

    Therefore, the expansion process is as follows:

1. new = old + old * (old / 2): expand capacity by 50% to get newCapacity

2. If newCapacity - minimum capacity (ie size + 1) <= 0, return the given minimum capacity. It should be noted that if the array is empty, the returned capacity is 10. Here it is explained in detail:

    Add elements to an empty list, size = 0, minCapacity = size + 1, the calculated newCapacity = 0 + 0 = 0, at this time newCapacity - minCapacity = -1, the condition is met. Then at this time, it is judged that the array is empty, so max(10,1), which is 10, is returned.

    Then continue to add elements, the capacity is 10, so the capacity will not be expanded until 10 elements are added. Until the 11th element is added, the expansion is triggered. At this time, the expansion is 50%.

return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);

    In most cases, return newCapacity, that is, expand the capacity by 50%. When the new capacity exceeds MAX_ARRAY_SIZE, it will go to the logic of hugeCapacity:

To sum up, the expansion process of ArrayList when adding elements:

1. The capacity of the newly created ArrayList is 10, which is actually 0.

2. When adding elements for the first time, the capacity is expanded to the default capacity of 10.

3. Until 10 elements are added, the capacity will not be expanded again, and the capacity of 10 will be maintained.

4. When the 11th element is added, a 50% expansion will be triggered; until the capacity is used up, the expansion will not continue, and so on.

3. Expansion demo verification 

    Next, we write a small demo to verify the expansion process:

public class ArrayListTest {
    public static void main(String[] args) {
        // 创建一个空的ArrayList
        ArrayList<Integer> list = new ArrayList<>();
        System.out.println("初始容量:" + getArrayListCapacity(list));
        for (int i = 0; i < 11; i++) {
            list.add(i);
            System.out.println("add第" + (i + 1) + "个元素,容量:" + getArrayListCapacity(list));
        }
    }

    /**
     * 反射获取Arraylist的容量
     * @param list
     * @return
     */
    private static int getArrayListCapacity(ArrayList<?> list) {
        try {
            java.lang.reflect.Field capacityField = ArrayList.class.getDeclaredField("elementData");
            capacityField.setAccessible(true);
            return ((Object[]) capacityField.get(list)).length;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
            return -1;
        }
    }

Guess you like

Origin blog.csdn.net/qq_21154101/article/details/132561458