【JavaSE】ArrayList expansion mechanism source code analysis

1. Overview of ArrayList

ArrayListIt is a commonly used data structure in the Java collection framework, and its bottom layer is implemented based on arrays. The size of the array is fixed, but ArrayListthe capacity expansion mechanism can realize the dynamic change of the capacity.

The capacity of the array is determined at the time of definition. If the array is full and then inserted, the array will overflow. Therefore, when inserting, it will first check whether expansion is required. If the current capacity + 1 exceeds the length of the array, expansion will be performed.

ArrayListThe expansion is to create a new array of 1.5 times , and then copy the value of the original array.

image-20230201153601407

ArrayListThere are two ways to add elements

  1. public boolean add(E e)
  2. public boolean addAll(Collection<? extends E> c)

The following source code analysis


2. Source code analysis of ArrayList construction method

When a collection is created ArrayList, its constructor is called

//存放元素的数据
transient Object[] elementData;
//默认空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
    
    };
//集合长度
private int size;

//无参构造
public ArrayList() {
    
    
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//指定元素长度构造
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);
    }
}

//构造参数为集合
public ArrayList(Collection<? extends E> c) {
    
    
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
    
    
        if (c.getClass() == ArrayList.class) {
    
    
            elementData = a;
        } else {
    
    
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
    
    
        // replace with empty array.
        elementData = EMPTY_ELEMENTDATA;
    }
}
  1. No-argument construction: No-argument construction will use DEFAULTCAPACITY_EMPTY_ELEMENTDATAan array as the data to store elements, that is, the length of the array at the bottom of the default collection is 0.
  2. Specified element length construction: The specified element length construction uses the construction parameter initialCapacityas the length of the array.this.elementData = new Object[initialCapacity];
  3. The construction parameter is a collection: use cthe length of the construction parameter collection as elementDatathe length of the underlying array, and copy the elements of the collection into a new array and assign it toelementData

3. ArrayList.add() source code analysis

First prepare a piece of code for adding data, the length of the added data is 15.

public class TestArrayList {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < 15; i++) {
    
    
            list.add(i);
        }
    }
}

First, ArrayListwhen creating a collection, its no-argument construction method is called, and ArrayListthe underlying array is an empty array with a length of 0.

The following is addthe source code of the method

public boolean add(E e) {
    
    
    //检查是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

Before adding elements into the collection, you need to call ensureCapacityInternalthe method to check whether to expand.

The parameter is the length of the array of underlying storage data + 1

private void ensureCapacityInternal(int minCapacity) {
    
    
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

ensureCapacityInternalinside ensureExplicitCapacitymethod.

Before execution, a method ensureExplicitCapacityneeds to be called calculateCapacityto calculate the length that needs to be expanded.

//默认的初始化容量大小
private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    
    
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    
    
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

calculateCapacityThe method has two parameters, elementDatawhich are the underlying array used to store data, and minCapacitythe new array length.

In this method, first judge elementDatawhether it is the default empty array (that is, whether it is DEFAULTCAPACITY_EMPTY_ELEMENTDATAthe same object) **, if so, take the maximum value of the length of the new array and the length of the default initialization capacity and return. **That is to say, if an element is added to the collection for the first time, the default capacity is 10.

If it is not the default empty array, it will minCapacityreturn the new array length.

//该参数是记录列表结构以被修改的次数
protected transient int modCount = 0;
private void ensureExplicitCapacity(int minCapacity) {
    
    
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

Then compare the size of the current new array length minCapacitywith the underlying array lengthelementData.length

If minCapacityit is less than or equal to elementData.length, no expansion is required.

If minCapacityit is larger elementData.length, it needs to be expanded and grow(minCapacity)the method is called.

The parameter is the length of the new array.

//数组最大容量,-8是因为有些VM会在数组中保留一些词
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
    
    
    // overflow-conscious code
    //记录底层数组的长度
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    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);
}

This growmethod is the key to expansion.

The key to the length of the expanded array is this line of code

int newCapacity = oldCapacity + (oldCapacity >> 1);

(oldCapacity >> 1)It is the right shift operation that means dividing by 2

Assuming that it is 10 now oldCapacity, its original code is 1010, moving one bit to the right is 0101, which is also 5.

So the new capacity after expansion newCapacityis 15.

Then judge the expanded capacity and compare newCapacityit with the capacity of the new array .minCapacity

  1. If it is less than, that is, the expanded capacity newCapacityis not enough, then assign the length of the new array minCapacityto newCapacity.

Then compare the expansion capacity newCapacitywith the maximum capacity of the arrayMAX_ARRAY_SIZE

  1. If greater than, execute hugeCapacity(minCapacity)the method
//数组最大容量,-8是因为有些VM会在数组中保留一些词
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
    
    
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

hugeCapacity(minCapacity)The method judges that if the minimum extension requirement value is less than 0, an exception is thrown; otherwise, it is judged minCapacitywhether the minimum extension requirement value is greater than MAX_ARRAY_SIZE, and if it is greater, returns intthe maximum value , and if not, returns MAX_ARRAY_SIZEthe value.

Finally, Arrays.copyOf(elementData, newCapacity);the expansion is completed.


4. ArrayList.addAll() source code analysis

Prepare a piece of test code first.

public class TestArrayList {
    
    
    public static void main(String[] args) {
    
    
        ArrayList list = new ArrayList();
        list.addAll(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
    }
}

The following is addAllthe source code of

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;
}

addAllThe main logic is to convert the incoming collection into an array and get the length of the array.

Then ArrayListcheck and expand the current operation, and then System.arraycopythe method System.arraycopyappends the array to the subscript elementDataof the array , then sets the size, and finally returns the addition status according to the length of the incoming container.sizesize


5. Summary

  1. ArrayListThe expansion is usually 1.5 times the size of the original array, and ArrayListthe maximum capacity is intthe maximum value.
  2. addAll(Collection c)When there is no element, the expansion is Math.max(10, 实际元素个数), and when there is an element, it isMath.max(原来容量的1.5倍,实际元素个数)
  3. add(Object o)The first expansion is 10, and the second expansion is 1.5 times the previous capacity.
  4. ArrayList(int initialCapacity)will use an array with the specified capacity
  5. ArrayList(Collection<? extends E> c)Will use the size of c as the array capacity

Guess you like

Origin blog.csdn.net/weixin_51146329/article/details/128837509