Interpretation of Java source code for literacy [collection--ArrayList]

1. Class description

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ArrayList is an array queue, which is equivalent to a dynamic array. Compared with an array, it can be expanded. It inherits AbstractList and implements List, RandomAccess, Cloneable, Serializable interfaces.

ArrayList inherits AbstractList and implements List, which is an array queue with basic functions such as adding, deleting, modifying, and traversing queries.

ArrayList implements RandomAccess interface and provides random access function. RandmoAccess is used by List in java to provide quick access function for List. ArrayList can quickly access element objects according to the serial number.

ArrayList implements the Cloneable interface, that is, it overrides the function clone() and can be cloned.

ArrayList implements the Serializable interface, indicating that ArrayList can be serialized for transmission.

ArrayList in particular is thread-unsafe.

Second, properties and construction methods

//default array storage capacity
private static final int DEFAULT_CAPACITY = 10;

/ / When the capacity of the specified array is 0, use this variable to assign a value
private static final Object[] EMPTY_ELEMENTDATA = {};

//Use this variable to assign values ​​when instantiating by default
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//The object array that actually stores the data, and the properties that are not modified by the serialized transient keyword are not serialized
transient Object[] elementData; // non-private to simplify nested class access

//The actual number of elements in the array, it is less than or equal to elementData.length
private int size;

//Maximum number of elements in the array
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * Constructor 1, if the specified capacity is allocated, the size of the specified capacity is allocated
 * Use EMPTY_ELEMENTDATA assignment if not specified
 */
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);
    }
}

/**
 * Constructor 2, use the default DEFAULTCAPACITY_EMPTY_ELEMENTDATA assignment
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * Construct an incoming collection as the data of the array
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

3. Common methods

(1) Add

There are two methods for adding. The call chain of the first add(E e) method involves 5 methods, which are as follows:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
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);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

Here is a step-by-step analysis. In the first step of calling the add(E e) method, we see that it calls the ensureCapacityInternal(size + 1) method. In this method, we first judge whether the array is an empty array with a length of 0. , if so, assign its capacity to the default capacity size of 10, and then call the ensureExplicitCapacity method, which records modCount+1, and judges whether the current capacity is less than the current length of the array, if it is greater than the current The length of the array begins to expand the operation and call the method grow(minCapacity). The length of the expansion is increased by half the size of the original array, and then it is judged whether the upper limit of the expansion of the array has been reached and assigned, and then the data of the old array is copied to The new array after expansion is assigned to the old array again, and finally the newly added element is assigned to the position of size+1 after expansion.

Then look at the second add method:

public void add(int index, E element) {
    rangeCheckForAdd(index);//Determine whether the array subscript is out of bounds

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //The System.arraycopy method is used here, and the meaning of the parameters is as follows:
    //(Original array, start position of original array, target array, start position of target array, number of copies)
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

The meaning of this method is to add an element to the specified position. ArrayList first checks whether the index is out of bounds. If it is not out of bounds, it checks whether it needs to be expanded, and then copies all the data after the index position as a whole to the starting position of index+1, and then The newly added data can be placed at the index position, and the data in front of the index does not need to be moved. Here we can see that inserting the data ArrayList into the specified position is a big action and consumes performance.

(2) delete

Delete specified unknown element according to subscript

public E remove(int index) {
    //Check if the subscript is out of bounds
    rangeCheck (index);
    // record the number of changes
    modCount++;
    //Get the value at the removed position
    E oldValue = elementData(index);
    //Get the number of elements to move
    int numMoved = size - index - 1;
    //Copy all the data moved to the index position
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //Assign the element at the position of size-1 to null, which is convenient for gc garbage collection
    elementData[--size] = null; // clear to let GC do its work
    //finally return the deleted data
    return oldValue;
}

delete by element

public boolean remove(Object o) {
    if (o == null) {//Remove null value
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {//Find the first element in the collection that is equal to null
                fastRemove(index);//Remove
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {//In the case of non-null, traverse each element and compare by equals
                fastRemove(index);//Remove
                return true;
            }
    }
    return false;
}

/*
 * This method is the same as the principle of removing by subscript, and the overall left shift
 */
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

Adding and removing elements may do a lot of data movement, so it is a relatively performance-intensive.

(3) Query (O(1))

public E get(int index) {
    //Check if the subscript is out of bounds
    rangeCheck (index);
    return elementData(index);
}

(4) Clear

public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

(5) Whether it contains

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

/**
 *
 */
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

Nothing to say about this

Summarize

The source code is not complicated, the bottom layer is an array, but this array can be expanded. Allows to add all elements including null, and all methods of ArrayList are thread-unsafe, you can use Collections.synchronizedList(list) to turn thread-safe.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325288751&siteId=291194637