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