Introduction of Java ArrayList related methods and source code analysis

Keywords: Java Fragment

Introduction to ArrayList:

The java.util.ArrayList is one of our most commonly used classes. At the bottom of the ArrayList is a dynamic array that readers can understand as an implementation of the array

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

}

As we can see from the code above, ArrayList inherits the AbstractList() abstract class and implements the List, RandomAccess, Cloneable, Serializable interfaces

AbstractList :

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {}

You can see that AbstractList inherits the AbstractCollection interface and implements the List interface

AbstractCollection :

public abstract class AbstractCollection<E> implements Collection<E> {}

AbstractCollection is an abstract class that implements the Collection interface and provides specific implementations of some methods.

Collection:

Collection is a top-level interface and is the top-level interface for many collection classes, inheriting Iterable and supporting lightweight traversal of its elements

public interface Collection<E> extends Iterable<E> {}

List :

ArrayList implements the List interface, which is also a top-level interface comparable to Collection and inherits the Collection interface

public interface List<E> extends Collection<E> {}

It is the parent of many collection classes,

eg:

List list = new ArrayList();
List list2 = new LinkedList();

RandomAccess

RandomAccess is also a top-level interface, and the classes that implement it support random access

Cloneable

The Cloneable interface is a top-level interface, and the classes that implement it support shallow copies

Serializable

Classes implementing this interface support serialization

The inheritance relationship between classes is shown in the diagram

Introduction to ArrayList related methods

trimToSize()

Code representation

Practice is the best way to test the truth:

import java.util.*;

/**
 * Details the basic usage of ArrayList
 */
public class ArrayListTest {

    private static class SortList implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            Integer i1 = Integer.valueOf(o1);
            Integer i2 = Integer.valueOf(o2);
            if(i1 < i2){
                return -1;
            }else if(i1 == i2){
                return 0;
            }
            return 1;
        }
    }

    // With variable parameters, you can accept any number of parameters
    public Set<String> putSet(String...args){
        Set<String> sets = new HashSet<>();
        for(String str : args){
            sets.add(str);
        }
        return sets;
    }

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("111");
        list.add("222");

        // Add elements at specified locations
        list.add(0,"333");
        System.out.println(list);

        // External Sorting
        list.sort(new SortList());
        System.out.println(list);

        list.clear();
        System.out.println(list.size());

        // Add elements using addAll
        ArrayListTest at = new ArrayListTest();
        list.addAll(at.putSet("1","2","3"));

        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
            // Remove all elements
            it.remove();
        }

        System.out.println("list Is it empty ? " + list.isEmpty());

        list.add("111");
        // Add a set collection at the specified location
        list.addAll(0,at.putSet("1","2","3"));
        System.out.println(list);

        // Whether to include the specified element
        if(list.contains("111")) {
            list.remove("111");
        }
        System.out.println(list);

        System.out.println(list.indexOf("1"));
        // Note that the subList() method is left open and right closed, many of which are similar in Java
        System.out.println(list.subList(0,3));

        // Expand list capacity
        list.ensureCapacity(10);
        // Remove list idle capacity
        list.trimToSize();

        // Get a specific element
        System.out.println(list.get(1));

        // Create a bi-directional list
        ListIterator<String> listIterator = list.listIterator();
        while(listIterator.hasNext()){
            // Move to end of list
            System.out.println(listIterator.next());
        }
        System.out.println("--------------------------");
        while (listIterator.hasPrevious()){
            // Move to the top of the list
            System.out.println(listIterator.previous());
        }

        // Convert list to array
        Object[] objects = list.toArray();
        System.out.println("objects = " + objects);

    }
}

Related Method Source Code Analysis

Specific analysis of the source code is based on the code example above, because just looking at the source code does not seem to understand anything, you need to debug the trace step by step according to the specific code

add() method

Interpretation: Add the specified element at the end of the list

    /**
     * Add the specified element at the end of the list
     */
    // Assume that "111" was added for the first time
    public boolean add(E e) {
        // Size is 0, so size + 1 passes 1
        ensureCapacityInternal(size + 1); 
        // elementData[0] = 111 , size++ = 1
        elementData[size++] = e;
        return true;
    }
    
    // This method is used for list expansion
    private void ensureCapacityInternal(int minCapacity) {
        // ElementmentData does not store elements at this time, 0
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // Then minCapacity takes the default initial capacity and the maximum value of minCapacity (the maximum values of 1 and 10)
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // Ensure clear capacity (minimum capacity compared to List elements)
        ensureExplicitCapacity(minCapacity);
    }

    // An element is added to the list, which results in structural modifications, which are explained below
    // At this point minCapacity is 10
    private void ensureExplicitCapacity(int minCapacity) {
        // Number of times + 1 
        // The number of times the structure has been modified (such as adding and deleting elements) in this list is represented by modCount. Structural modification refers to the ability to
        // An operation that changes the capacity of a list, or otherwise changes it, causes the traversal process to produce incorrect results.
        modCount++;

        // overflow-conscious code
        // 10 - 0 > 0 walk the group method
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * Increase capacity to ensure adequate element capacity
     * 
     * Parameter passed in 10
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        // oldCapacity = 0
        int oldCapacity = elementData.length;
        // newCapacity = 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // newCapacity - minCapacity = -10
        if (newCapacity - minCapacity < 0)
            // newCapacity = 10
            newCapacity = minCapacity;
        
        // MAX_ARRAY_SIZE = Maximum space allocated by array = 2147483639
        // Normally no larger than MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // The bottom level is System.arraycopy(), and readers of System.arrayCopy() can refer to another of my blogs
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    

Values of the basic data type wrapper classes commonly used: Values commonly used by Java basic data type wrapper classes

add(int index, E element)

Interpretation: Insert the specified element at the specified location in the list, and move the element at the current location if there is one at the current location

    /**
     * Inserts the specified element at the specified location in the list, and moves the element at the current location if there is one at the current location
     * Position of all elements after the position to be inserted forward + 1
     *
     */
    public void add(int index, E element) {
        // Check if 0 is out of bounds
        rangeCheckForAdd(index);

        // No more, the reader can debug himself
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // Because elements are inserted from the current position, the current position and elements that follow move backwards
        // Array copy using System.arraycopy
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // Assign a value to the current element
        elementData[index] = element;
        size++;
    }

    /**
     * Range checks provided for add and addall, which do not meet the criteria, throw an IndexOutOfBoundsException exception
     */
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    

Clear()

Interpretation: Remove all elements from the list

    /**
     * Remove all elements from the list list list, and the list becomes empty after calling this method
     *
     */
    public void clear() {
        // Modifications + 1
        modCount++;

        // clear to let GC do its work
        // Leave each variable blank and GC recycles it
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        // The length of the list becomes zero
        size = 0;
    }

The source code for this method is easier to understand

addAll(Collection<? extends E> c)

Interpretation: Add a Collection collection to the end of the list

    /**
     * Adds a Collection collection (a class that implements this interface) to the end of the list and returns in the order of iteration.
     * The behavior of this operation is if Collection (a class that implements this interface) is modified during this method call.
     * Then this operation will not succeed
     */
    public boolean addAll(Collection<? extends E> c) {
        // Convert Collection to Object[] Array
        Object[] a = c.toArray();
        // There are three elements in the array
        int numNew = a.length;
        // Since the above operation calls the list.clear() method once, list.size = 0
        ensureCapacityInternal(size + numNew);  // Increments modCount
        // One sentence explanation: Copy the elements in the a array at 0 positions to the elements in the size position in the elementData array,
        // The length of the copy is numNew
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

    // toArray() method: 

    /**
     * Returns an array containing all elements from the first to the last
     * The returned array is "safe" because the list has no references to maintain (in other words, this method must assign a new array)
     * The caller can therefore modify the returned array arbitrarily
     * This method is a bridge between an array and a collection
     */
    public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

iterator(), hasNext(), next()

Interpretation: The Iterator method traverses elements in the list, returns an Itr internal class, the hasNext() method determines if there are any elements in the list that are not traversed, and the next() method gets the next element

    /**
     * Returns iterators for elements in this list in the appropriate order
     * The returned iterator supports the fail-fast mechanism
     */
    public Iterator<E> iterator() {
        return new Itr();
    }

    /**
     * Itr Is an internal class that implements the Iterator interface and supports fast traversal
     */
    private class Itr implements Iterator<E> {
        // Subscript returned by next element
        int cursor;       // index of next element to return
        // Subscript returned by the last element, if -1 is not returned
        int lastRet = -1; // index of last element returned; -1 if no such
        // ExpctedModCount expects the same number of modifications as modCount (used by iterator s to determine the fail-fast mechanism)
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            // Determine whether fail-fast mechanism is triggered during traversal
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            // If lastRet < 0, lastRet is not changed.
            // So remove() should have been called without calling next()
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        // Throw an exception if the number of modifications does not meet the expected number of modifications
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

addAll(int index,Collection<? extends E> c)

Interpretation: Add Collection Collection a location

    /**
     * Insert a Collection collection at the specified location subscript
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        // Number of elements to move
        int numMoved = size - index;
        if (numMoved > 0)
            // The first array copy, starting at the index position in elementData, to the index + numNew position, copies numMoved elements
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        // The second copy of the array, starting at position 0 in the array a, copies to you at index position of elementData, and copies numNew elements
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

    

contains(Object o)

Interpretation: Determine if a list list list contains an element

    /**
     * Returns true if the list contains the specified element
     * Further, return true if and only if the list contains at least one element
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    /**
     * Returns the subscript value of the specified element that first appears in the list, or -1 if it does not contain the specified element.
     * Further, return the smallest index when (o == null? Get (i) == null: o.equals (get (i))
     * Or return -1 without this subscript value
     *
     */
    public int indexOf(Object o) {
        // If o equals null, it determines if there is an empty element in the elementData and, if so, returns
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            // If not null, return the location where the value is stored
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

remove(Object o)

Interpretation: Remove an element from the list

    /**
     * If it exists, remove one of the first elements in the list.If the list does not contain the specified element, it will not change
     *
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    //Quickly remove a specified element
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private removal method, and does not return the removed element, this source code is simple
     */
    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
    }

indexOf(Object o)

Interpretation: Retrieve the location of an element

This source is the same as the indexOf source called in contains(Object o)

subList(int fromIndex, int toIndex)

Interpretation: Return a fragment of the list list list

    /**
     * Returns a portion of the list list view (equivalent to a list fragment), [fromIndex, toIndex], if fromIndex and
     * toIndex In the same way, the list is empty, the returned list is returned, so there is no structural change in the list
     * This returned list fragment supports all list operations
     */
    public List<E> subList(int fromIndex, int toIndex) {
        // subList Scope Check
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

    private class SubList extends AbstractList<E> implements RandomAccess {
        
        private final AbstractList<E> parent;
        private final int parentOffset;
        private final int offset;
        int size;

        SubList(AbstractList<E> parent,
                int offset, int fromIndex, int toIndex) {
            this.parent = parent;
            this.parentOffset = fromIndex;
            this.offset = offset + fromIndex;
            this.size = toIndex - fromIndex;
            this.modCount = ArrayList.this.modCount;
        }
    }

ensureCapacity(int minCapacity)

Explanation: Expand list capacity

    /**
     * Increase the capacity of the ArrayList instance, if necessary, to ensure that it holds the minimum capacity elements
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

trimToSize()

Interpretation: Remove list idle capacity

    /**
     * Remove extra space from ArrayList
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

sort(Comparator<? super E> c)

The sort method receives a custom comparer for a custom comparison. Here's the source code

        @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        // Based on the code analysis above, modCounnt has been modified three times at this time (three elements added)
        final int expectedModCount = modCount;
        // Outer Sort of Array
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

Posted by theredking on Thu, 30 May 2019 10:01:12 -0700