1. Class definition

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

You can see that by definition

  • ArrayList is a generic class
  • ArrayList inherits the AbstractList abstract class
  • ArrayList implements the List interface
  • ArrayList implements the RandomAccess interface, which indicates that ArrayList supports RandomAccess. In fact, ArrayList is an array of type Object
  • ArrayList implements the Cloneable interface, which means ArrayList supports cloning
  • ArrayList implements the java.io.Serializable interface, indicating that ArrayList supports serialization

2. Field attributes

	// Serialize the version number
    private static final long serialVersionUID = 8683452581122892189L;
	// The default capacity is 10
    private static final int DEFAULT_CAPACITY = 10;
	// Default empty array with capacity 0
    private static final Object[] EMPTY_ELEMENTDATA = {};
	// Default empty array when initialized, because it is possible to hold no elements
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	// DEFAULT_CAPACITY (10) where the data is stored.
	// TRANSIENT modifier indicates that serialization is ignored
    transient Object[] elementData; // non-private to simplify nested class access
	// The length of valid data is not necessarily equal to the length of elementData
    private int size;
	// Maximum capacity
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	// The number of changes inherited from the parent class
    protected transient int modCount = 0;
Copy the code

constructor

 	// Specifies the capacity constructor
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            // If the specified capacity is greater than 0, an array of the specified capacity is created
            // Note that the array type is not generic, but Object
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            // If the specified capacity is 0, the default EMPTY_ELEMENTDATA is used
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            If the capacity is less than 0, an exception is thrown
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}// Constructor with no arguments, initialized with DEFAULTCAPACITY_EMPTY_ELEMENTDATA by default
    public ArrayList(a) {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

	// Pass in a collection as the argument's constructor
    public ArrayList(Collection<? extends E> c) {
        // Convert the collection contents to an array and assign values to elementData
        elementData = c.toArray();
        // Assign size
        if((size = elementData.length) ! =0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            // If the collection passed in is not of type Object, the new array should be copied and assigned to elementData
            if(elementData.getClass() ! = Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); }else {
            // Assign EMPTY_ELEMENTDATA to elementData if length is 0
            this.elementData = EMPTY_ELEMENTDATA; }}Copy the code

You can see that from above

  • An ArrayList can be initialized by passing in a capacity of type int
  • ArrayList has constructors that take no arguments by default
  • You can pass in a Collection object to initialize an ArrayList
  • No passing in capacity DEFAULTCAPACITY_EMPTY_ELEMENTDATA is used as the elementData value by default, and EMPTY_ELEMENTDATA is used as the elementData value for capacity 0

Method 4.

The size method

public int size(a) {
    	// Return size, indicating that size is the available element length
        return size;
    }
Copy the code

IsEmpty method

public boolean isEmpty(a) {
    	// If size equals 0, ArrayList is empty
        return size == 0;
    }
Copy the code

The contains method

public boolean contains(Object o) {
    	// Call indexOf directly. If the value is greater than or equal to 0, it exists
        return indexOf(o) >= 0;
    }
Copy the code

Method indexOf

 public int indexOf(Object o) {
        if (o == null) {
            // If null, use == to determine equality
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            // If not empty, use equals to determine whether it is equal
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
Copy the code
  • The lastIndexOf method works the same way as indexOf except that the for loop is incremented and decremented

Clone method

public Object clone(a) {
        try {
            ArrayList is cloned as a shallow copy. If it is a reference type, only references are copiedArrayList<? > v = (ArrayList<? >)super.clone();
            // Assign the value of elementData
            v.elementData = Arrays.copyOf(elementData, size);
            // Set the modified count to 0
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw newInternalError(e); }}Copy the code

ToArray method

 public Object[] toArray() {
     	// Call Array's copyOf method to copy a new Array
        return Arrays.copyOf(elementData, size);
    }
 // Pass in an array of targets
 public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // If the target array is smaller than size, create a new array of size length
            // Assign the contents of element to the new array, return the new array
            // array.copyof also calls System. Arraycopy
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
     	// If the length of the target array is greater than or equal to size, the contents of elementData are directly assigned to the target array
        System.arraycopy(elementData, 0, a, 0, size);
     	// If the target array is larger than size, set size to null
        if (a.length > size)
            a[size] = null;
        return a;
    }
Copy the code

The get method

public E get(int index) {
    	// Check if the array subscript is out of bounds
        rangeCheck(index);
    	// Return the value of the destination location
        return elementData(index);
    }
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
E elementData(int index) {
        return (E) elementData[index];
    }
Copy the code

Set method

public E set(int index, E element) {
    	// Check if the array subscript is out of bounds
        rangeCheck(index);
    	// Get the old value
        E oldValue = elementData(index);
    	// Set the new value
        elementData[index] = element;
    	// Return the old value
        return oldValue;
    }
Copy the code

The add method

public boolean add(E e) {
        // Check the capacity. If the capacity is insufficient, expand the capacity
        ensureCapacityInternal(size + 1);  // Increments modCount!!
    	// Assign and add one to the available element statistics
        elementData[size++] = e;
        return true;
    }

public void add(int index, E element) {
    	// check the validity of subscripts
        rangeCheckForAdd(index);
        // Check the capacity. If the capacity is insufficient, expand the capacity
        ensureCapacityInternal(size + 1);  // Increments modCount!!
    	// Use system. arrayCopy to move the element in the index position back one position to free up the index position
    	// Poor performance
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
    	// Assign the position of index
        elementData[index] = element;
    	// Add 1 to available element statistics
        size++;
    }
// Check the capacity. If the capacity is insufficient, expand the capacity
private void ensureCapacityInternal(int minCapacity) {
    	// If elementData is the default initialized capacity, the default capacity and the passed capacity are used to compare whether to expand
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    	// Check whether the capacity is expanded
        ensureExplicitCapacity(minCapacity);
    }
// Check whether the capacity is expanded
private void ensureExplicitCapacity(int minCapacity) {
    	// Change the number by 1
        modCount++;
    	// If the required capacity is greater than the length of elementData, it needs to be expanded
        if (minCapacity - elementData.length > 0)
            / / capacity
            grow(minCapacity);
    }
// Expansion function, this is the main point
// The capacity is expanded by 1.5 times by default
private void grow(int minCapacity) {
        // Get the previous capacity
        int oldCapacity = elementData.length;
    	// The new capacity is 1.5 times longer than the old capacity
    	//oldCapacity >> 1 equals oldCapacity/2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
    	// If the capacity is not enough, replace the new capacity with the incoming capacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
    	// If the new capacity is larger than the maximum capacity that can be set, check the maximum capacity
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // Arrays.copyof is used for capacity expansion
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
// Maximum capacity check
private static int hugeCapacity(int minCapacity) {
    	// If the capacity exceeds integer. MAX_VALUE, an exception is thrown
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
    	// If the value is greater than the default maximum, integer. MAX_VALUE is returned; otherwise, MAX_ARRAY_SIZE is returned
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
// Check subscript validity
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
Copy the code

The remove method

public E remove(int index) {
    	// subscript check
        rangeCheck(index);
    	// Modify statistics by 1
        modCount++;
    	// Get the old value
        E oldValue = elementData(index);
    	// To count the number of moves, delete the element after the deleted position is moved forward by one position, and set the last value to NULL
        int numMoved = size - index - 1;
    	// If the number of moves is greater than 0, the last element is removed if it is equal to 0
        if (numMoved > 0)
            // Move the index+1 position and all subsequent elements forward one bit
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
    	// Set the trailing value to 0 and subtract 1 from the number of available elements
        elementData[--size] = null; // clear to let GC do its work
    	// Return the old value
        return oldValue;
    }

public boolean remove(Object o) {
        if (o == null) {
            // If the element is empty, use == to compare
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    // Remove if found
                    fastRemove(index);
                    return true; }}else {
            // The element is not empty. Equals is used to compare the elements
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                     // Remove if found
                    fastRemove(index);
                    return true; }}// Return true if it exists, false if it does not
        return false;
    }

private void fastRemove(int index) {
    	// Modify statistics by 1
        modCount++;
    	// To count the number of moves, delete the element after the deleted position is moved forward by one position, and set the last value to NULL
        int numMoved = size - index - 1;
    	// If the number of moves is greater than 0, the last element is removed if it is equal to 0
        if (numMoved > 0)
            // Move the index+1 position and all subsequent elements forward one bit
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
    	// Set the trailing value to 0 and subtract 1 from the number of available elements
        elementData[--size] = null; // clear to let GC do its work
    }
Copy the code
  • Note: The remove(Object 0) method removes only one element

Clear method

public void clear(a) {
    	// Modify statistics by 1
        modCount++;
        // Iterate over and set all elements to null
        for (int i = 0; i < size; i++)
            elementData[i] = null;
    	// Set the available elements to 0
        size = 0;
    }
Copy the code

AddAll method

public boolean addAll(Collection<? extends E> c) {
    	// Convert the Collection passed to an array
        Object[] a = c.toArray();
    	// Get the converted length
        int numNew = a.length;
    	// Check capacity and expand capacity
        ensureCapacityInternal(size + numNew);  // Increments modCount
    	// Append the transformed array contents to elementData using system. arrayCopy
        System.arraycopy(a, 0, elementData, size, numNew);
    	// Recalculate the available element sizes
        size += numNew;
    	// Return true if the converted length is not equal to 0, false otherwise
        returnnumNew ! =0;
    }

public boolean addAll(int index, Collection<? extends E> c) {
    	// check subscript validity
        rangeCheckForAdd(index);
		// Convert the Collection passed to an array
        Object[] a = c.toArray();
    	// Get the converted length
        int numNew = a.length;
    	// Check capacity and expand capacity
        ensureCapacityInternal(size + numNew);  // Increments modCount
		// Count the number of moves
        int numMoved = size - index;
        if (numMoved > 0)
            // Use system. arrayCopy to move the index position and subsequent elements back by numNew bits
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
		// Copy the contents of a to elementData, starting at index
        System.arraycopy(a, 0, elementData, index, numNew);
    	// Recalculate the available element sizes
        size += numNew;
    	// Return true if the converted length is not equal to 0, false otherwise
        returnnumNew ! =0;
    }
Copy the code

RemoveAll method

public boolean removeAll (Collection
        c) {
    	// Check the argument. If the argument is empty, throw an exception
        Objects.requireNonNull(c);
    	// Batch remove
        return batchRemove(c, false);
    }

private boolean batchRemove(Collection<? > c,boolean complement) {
    	/ / copy elementData
        final Object[] elementData = this.elementData;
    	//r represents the traversal value, w represents the number of conditions
        int r = 0, w = 0;
    	// Whether to modify
        boolean modified = false;
        try {
            / / traverse elementData
            for (; r < size; r++)
                // Complement false adds the element to elementData if c does not contain it
                if (c.contains(elementData[r]) == complement)
                    // Add w + 1 to the start of elementData if the conditions are met
                    elementData[w++] = elementData[r];
        } finally {
            //r not equal to size means an exception was thrown at position r
            C. contains(elementData[r]) == complement
            if(r ! = size) {// copy the values after r to w
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                // add r to size to w
                w += size - r;
            }
            // If w is not equal to size, some of the conditions are met
            // set the part after w to null
            if(w ! = size) {// clear to let GC do its work
                // set the part after w to null
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                // Recalculate to modify the statistics
                modCount += size - w;
                // Recalculate the number of currently available elements
                size = w;
                // Set to be modified
                modified = true; }}return modified;
    }
Copy the code

SubList method

public List<E> subList(int fromIndex, int toIndex) {
    	// check the validity of subscripts
        subListRangeCheck(fromIndex, toIndex, size);
    	// Returns a SubList, a private inner class of ArrayList
        return new SubList(this.0, fromIndex, toIndex);
    }
Copy the code

The forEach method

 @Override
    public void forEach(Consumer<? super E> action) {
        // Check if action is empty and throw an exception if it is empty
        Objects.requireNonNull(action);
        // Copy modCount with final
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        // The for loop calls the accept of the action
       	ExpectedModCount == expectedModCount Ensures that the ArrayList in the for loop is not modified
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        // If the ArrayList in forEach is modified, throw an exception
        if(modCount ! = expectedModCount) {throw newConcurrentModificationException(); }}Copy the code

RemoveIf method

 @Override
    public boolean removeIf(Predicate<? super E> filter) {
        // Check whether filter is empty
        Objects.requireNonNull(filter);
        // Initializes the number of removals to 0
        int removeCount = 0;
        // Use BitSet to save the tabs to be removed
        final BitSet removeSet = new BitSet(size);
        // Make a copy of modCount and modify it with final to ensure that the ArrayList is not modified externally
        final int expectedModCount = modCount;
        // Make a copy of size
        final int size = this.size;
        //for loop traversal
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            // Gets the element of the current traversal position
            final E element = (E) elementData[i];
            // Determine whether the element meets the deletion criteria
            if (filter.test(element)) {
                // Record the subscript to removeSet
                removeSet.set(i);
                // The number of deletions increases by 1removeCount++; }}// Throw an exception if it is modified midway
        if(modCount ! = expectedModCount) {throw new ConcurrentModificationException();
        }

     	// Determine if there are elements that meet the deletion criteria
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {
            // Count the number of new available elements
            final int newSize = size - removeCount;
            // The use of BitSet is not clear, in this case it is supposed to save the subscript
            // The for loop probably copies the undeleted items to the front
            // Set everything after newSize to null
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            // Leave the tail element empty. The number of valid elements is only newSize
            for (int k=newSize; k < size; k++) {
                elementData[k] = null;  // Let gc do its work
            }
            // Count the new valid elements
            this.size = newSize;
            // Throw an exception if it is modified midway
            if(modCount ! = expectedModCount) {throw new ConcurrentModificationException();
            }
            modCount++;
        }

        return anyToRemove;
    }
Copy the code

ReplaceAll method

@Override
    @SuppressWarnings("unchecked")
    public void replaceAll(UnaryOperator<E> operator) {
        // Check whether operator is null and throw an exception if null
        Objects.requireNonNull(operator);
        // Make a copy of modCount and modify it with final to ensure that the ArrayList is not modified externally
        final int expectedModCount = modCount;
        // Make a copy of size
        final int size = this.size;
        //for loop traversal
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            // Call operator.apply to operate on each element
            elementData[i] = operator.apply((E) elementData[i]);
        }
        // Throw an exception if modified
        if(modCount ! = expectedModCount) {throw new ConcurrentModificationException();
        }
        // Modify statistics by 1
        modCount++;
    }
Copy the code

Sort method

@Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        // Make a copy of modCount and modify it with final to ensure that the ArrayList is not modified externally
        final int expectedModCount = modCount;
        // arrays.sort is called to sort
        Arrays.sort((E[]) elementData, 0, size, c);
        // Throw an exception if modified
        if(modCount ! = expectedModCount) {throw new ConcurrentModificationException();
        }
        modCount++;
    }
Copy the code

The iterator method

public Iterator<E> iterator(a) {
        return new Itr();
    }
Copy the code
  • Itr is an inner class of ArrayList
  • Itr class definition
private class Itr implements Iterator<E>
Copy the code

Method listIterator

public ListIterator<E> listIterator(a) {
        return new ListItr(0);
    }

 public ListIterator<E> listIterator(int index) {
        if (index < 0 || index > size)
            throw new IndexOutOfBoundsException("Index: "+index);
        return new ListItr(index);
    }
Copy the code
  • ListItr is an inner class of ArrayList
  • ListItr class definition
private class ListItr extends Itr implements ListIterator<E>
Copy the code