ArrayList source code analysis

The ArrayList class is too simple for Android developers: it is a collection class that stores elements. For example, the common usage is like this: List list = new ArrayList(); We can perform some basic operations on the list collection - addition, deletion, modification and inspection. Of course, these only stop at using ArrayList. Do you understand the internal implementation principle of ArrayList? Do you understand its internal CRUD mechanism? Are you familiar with its expansion mechanism? If you know, you can skip the following content; if you don't, let's continue driving.

1. Analysis of ArrayList source code

Open the ArrayList source code of Android SDK, we first start with its constructor:
1. Constructor
From the source code, we can see that ArrayList has three constructors, which are as follows:
a). No-parameter constructor

/**
     * Constructs a new {@code ArrayList} instance with zero              initial capacity.
     */
    public ArrayList() {
        array = EmptyArray.OBJECT;
    }

Among them, array is a member variable of ArrayList, which is transient Object[] array; it is an array
, so we know that ArrayList is implemented internally by an array.
b). The formal parameter is an integer constructor

/**
     * Constructs a new instance of {@code ArrayList} with the specified
     * initial capacity.
     *
     * @param capacity
     *            the initial capacity of this {@code ArrayList}.
     */
    public ArrayList(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("capacity < 0: " + capacity);
        }
        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
    }

First, determine whether the capacity is less than 0. If it is less than 0, an IllegalArgumentException is thrown; if it is equal to 0, an array of the same size as the no-argument constructor is created; if it is greater than 0, an array of size capacity is created.
c). The formal parameter is a constructor of a collection

/**
     * Constructs a new instance of {@code ArrayList} containing the elements of
     * the specified collection.
     *
     * @param collection
     *            the collection of elements to add.
     */
    public ArrayList(Collection<? extends E> collection) {
        if (collection == null) {
            throw new NullPointerException("collection == null");
        }

        Object[] a = collection.toArray();
        if (a.getClass() != Object[].class) {
            Object[] newArray = new Object[a.length];
            System.arraycopy(a, 0, newArray, 0, a.length);
            a = newArray;
        }
        array = a;
        size = a.length;
    }

First determine whether the collection is empty, if it is empty, a NullPointerException will be thrown; otherwise, convert the collection into an array and assign it to the local array a, then determine whether a is an Object array type, such as an Object array type, and finally assign a to The temporary variable array of ArrayList, and the value of the member variable size is equal to the length of the array a.

Next, let's take a look at how ArrayList adds elements:
2. ArrayList's add method
ArrayList's add method also has four overloading methods, let's look at them separately:
a), the formal parameter is a parameter (the parameter is an Object)

/**
     * Adds the specified object at the end of this {@code ArrayList}.
     *
     * @param object
     *            the object to add.
     * @return always true
     */
    @Override public boolean add(E object) {
        Object[] a = array;
        int s = size;
        if (s == a.length) {
            Object[] newArray = new Object[s +
                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
                     MIN_CAPACITY_INCREMENT : s >> 1)];
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        a[s] = object;
        size = s + 1;
        modCount++;
        return true;
    }

First, assign the array to the local a array, and assign the size to the local variable a, and then judge whether s is equal to the length of the a array. The capacity value is judged by a ternary expression, first judge whether the value of s is less than MIN_CAPACITY_INCREMENT / 2, where the default value of MIN_CAPACITY_INCREMENT is 12, that is, whether s is less than 6, if the value of s is less than 6, the new array The size is s+12, otherwise the size of the new array is s+(half the size of s). Then assign the elements of a to newArray, and modify the values ​​of array and a. Finally, put the object in the a[s] position, add 1 to the size, and increment modCout by 1, where modCount records the number of modifications.

b) The formal parameter is two parameters

/**
     * Inserts the specified object into this {@code ArrayList} at the specified
     * location. The object is inserted before any previous element at the
     * specified location. If the location is equal to the size of this
     * {@code ArrayList}, the object is added at the end.
     *
     * @param index
     *            the index at which to insert the object.
     * @param object
     *            the object to add.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location > size()}
     */
    @Override public void add(int index, E object) {
        Object[] a = array;
        int s = size;
        if (index > s || index < 0) {
            throwIndexOutOfBoundsException(index, s);
        }

        if (s < a.length) {
            System.arraycopy(a, index, a, index + 1, s - index);
        } else {
            // assert s == a.length;
            Object[] newArray = new Object[newCapacity(s)];
            System.arraycopy(a, 0, newArray, 0, index);
            System.arraycopy(a, index, newArray, index + 1, s - index);
            array = a = newArray;
        }
        a[index] = object;
        size = s + 1;
        modCount++;
    }

This add method means inserting the object value at the index position, that is, inserting a value at the specified position. First, assign array to a, size to s, and then determine whether the index is out of bounds, if yes, throw an out-of-bounds exception; otherwise, if s is less than the length of a, the index of the a array will start to the s-index position. The element is assigned to the element after the index of a; if s is greater than the length of a, a newArray array is created, and its size is also the size after the expansion mechanism. The implementation is the same as the above, and will not be described in detail here. Then copy the elements from 0 to index position in array a to newArrayList, then copy the elements from index to s-index position in array a to newArrayList, assign newArray to array and a, and finally put object into a[s] In the position, size is incremented by 1, and modCount is incremented by 1.
c), the formal parameter is a parameter (is a set)

 /**
     * This method controls the growth of ArrayList capacities.  It represents
     * a time-space tradeoff: we don't want to grow lists too frequently
     * (which wastes time and fragments storage), but we don't want to waste
     * too much space in unused excess capacity.
     *
     * NOTE: This method is inlined into {@link #add(Object)} for performance.
     * If you change the method, change it there too!
     */
    private static int newCapacity(int currentCapacity) {
        int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
                MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
        return currentCapacity + increment;
    }

    /**
     * Adds the objects in the specified collection to this {@code ArrayList}.
     *
     * @param collection
     *            the collection of objects.
     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
     *         otherwise.
     */
    @Override public boolean addAll(Collection<? extends E> collection) {
        Object[] newPart = collection.toArray();
        int newPartSize = newPart.length;
        if (newPartSize == 0) {
            return false;
        }
        Object[] a = array;
        int s = size;
        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
        if (newSize > a.length) {
            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
            Object[] newArray = new Object[newCapacity];
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        System.arraycopy(newPart, 0, a, s, newPartSize);
        size = newSize;
        modCount++;
        return true;
    }

The first is to convert the collection into an array and put it in newPart, where newPartSize represents the size of the newPart array, then assign the array to the local array a, and assign the size to s. The size of the new array is equal to s+newPartSize, and then determine whether the new array is greater than a. length, if it is greater than the size of the new array, expand it according to the size of the new array, copy the elements in a to newArray, and assign newArray to array and a. Then copy the elements in newPart to the s of a to the position of newPartSize, the size is equal to newSize , modCount is incremented by 1.

d), add a collection at the specified location

/**
     * Inserts the objects in the specified collection at the specified location
     * in this List. The objects are added in the order they are returned from
     * the collection's iterator.
     *
     * @param index
     *            the index at which to insert.
     * @param collection
     *            the collection of objects.
     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
     *         otherwise.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location > size()}
     */
    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        int s = size;
        if (index > s || index < 0) {
            throwIndexOutOfBoundsException(index, s);
        }
        Object[] newPart = collection.toArray();
        int newPartSize = newPart.length;
        if (newPartSize == 0) {
            return false;
        }
        Object[] a = array;
        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
        if (newSize <= a.length) {
             System.arraycopy(a, index, a, index + newPartSize, s - index);
        } else {
            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
            Object[] newArray = new Object[newCapacity];
            System.arraycopy(a, 0, newArray, 0, index);
            System.arraycopy(a, index, newArray, index + newPartSize, s-index);
            array = a = newArray;
        }
        System.arraycopy(newPart, 0, a, index, newPartSize);
        size = newSize;
        modCount++;
        return true;
    }

First assign size to s, if the index is out of bounds, throwIndexOutOfBoundsException is thrown. Convert the collection into an array and put it in newPart, newPartSize represents the size of the newPart array, and then assign the array to a, newSize is equal to the length of s+newPart, and judge whether newSize is less than the length of a, if it is less than, then directly start with the index in a The element is placed at the starting position of index+newPartSize; on the contrary, go down, expand according to newSize, create a newArray, and then copy the elements from 0 to index in a to newArray, and copy all elements after index in a to index+ in newArray newPartSize to the s-index position, and finally assign newArray to array and a, copy the elements in newPart to the index of a to the newPartSize position, size is equal to newSize, and modCount is incremented by 1.

Next, let's take a look at how ArrayList removes elements:
3. ArrayList's remove method
a), the formal parameter is an integer subscript

/**
     * Removes the object at the specified location from this list.
     *
     * @param index
     *            the index of the object to remove.
     * @return the removed object.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location >= size()}
     */
    @Override public E remove(int index) {
        Object[] a = array;
        int s = size;
        if (index >= s) {
            throwIndexOutOfBoundsException(index, s);
        }
        @SuppressWarnings("unchecked") E result = (E) a[index];
        System.arraycopy(a, index + 1, a, index, --s - index);
        a[s] = null;  // Prevent memory leak
        size = s;
        modCount++;
        return result;
    }

First, assign the array to the local array a, and assign the size to s. If the index is greater than or equal to s, it is an out-of-bounds exception. Then move the element at the starting position of index one position forward, a[s] is empty, and the size is reduced by 1. modCount is incremented by 1. Returns the removed element.
b), the formal parameter is an object

@Override public boolean remove(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        }
        return false;
    }

First, assign the array to the local array a, and assign the size to s, and then judge whether the object is empty. If it is not empty, traverse a. When a[i] is equal to object, move the element after the i position forward by one position. , then empty a[s], decrease size by 1, increase modCount by 1, and then continue to loop until the traversal is complete. If the object is empty, the principle is the same as before, and will not be described here.

4. The removeRange method of ArrayList

@Override protected void removeRange(int fromIndex, int toIndex) {
        if (fromIndex == toIndex) {
            return;
        }
        Object[] a = array;
        int s = size;
        if (fromIndex >= s) {
            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
                    + " >= size " + size);
        }
        if (toIndex > s) {
            throw new IndexOutOfBoundsException("toIndex " + toIndex
                    + " > size " + size);
        }
        if (fromIndex > toIndex) {
            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
                    + " > toIndex " + toIndex);
        }

        System.arraycopy(a, toIndex, a, fromIndex, s - toIndex);
        int rangeSize = toIndex - fromIndex;
        Arrays.fill(a, s - rangeSize, s, null);
        size = s - rangeSize;
        modCount++;
    }

First determine whether fromIndex is equal to toIndex, if it is equal to return directly. Go down, assign the array to a, and assign the size to s. If fromIndex or toIndex is greater than s, and fromIndex is greater than toIndex, an exception is thrown, and then the element starting with toIndex in a is moved forward by rangeSize positions and s-rangeSize in a The element from the beginning to the s position is empty, the size becomes s-rangeSize, and modCount is incremented by 1.

5. The set method of ArrayList

/**
     * Replaces the element at the specified location in this {@code ArrayList}
     * with the specified object.
     *
     * @param index
     *            the index at which to put the specified object.
     * @param object
     *            the object to add.
     * @return the previous element at the index.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location >= size()}
     */
    @Override public E set(int index, E object) {
        Object[] a = array;
        if (index >= size) {
            throwIndexOutOfBoundsException(index, size);
        }
        @SuppressWarnings("unchecked") E result = (E) a[index];
        a[index] = object;
        return result;
    }

To modify the element at the specified position, first assign the array value to a. If the index is greater than the size, an out-of-bounds exception will be thrown. The result records the value of the element at the position before the modification, and then the value of the position element is set to object, and the result is returned.

6. The clear method of ArrayList

/**
     * Removes all elements from this {@code ArrayList}, leaving it empty.
     *
     * @see #isEmpty
     * @see #size
     */
    @Override public void clear() {
        if (size != 0) {
            Arrays.fill(array, 0, size, null);
            size = 0;
            modCount++;
        }
    }

If size is not equal to 0, the elements at each position are filled with null, size is set to 0, and modCount is incremented by 1.

Well, this is the end of the source code analysis of ArrayList. The technical level is limited, and criticisms and corrections are welcome. Thank you.

Guess you like

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