/** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; It can be seen that ArrayList is implemented based on the pattern of array
1. Initial space size of ArrayList
You can see the declared initial capacity in the ArrayList source code
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
From the source code, we can get that the initial capacity of ArrayList is 10
2. add() - > append operation of ArrayList (append at the end)
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { // Determine the internal capacity, in order to ensure that the internal capacity is enough to store additional elements (if the capacity is enough, the capacity will not be processed, if the capacity is not enough, the capacity will be expanded by one) ensureCapacityInternal(size + 1); // Increments modCount!! // Append a new element to the collection elementData[size++] = e; return true; } ================================================================================== private void ensureCapacityInternal(int minCapacity) { // Get computing capacity here ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } ================================================================================= private static int calculateCapacity(Object[] elementData, int minCapacity) { // Determine whether the element in ArrayList is the initial null value if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // If the adding element is the first time, the initial capacity will be returned directly // Math.max() is the maximum of two numbers return Math.max(DEFAULT_CAPACITY, minCapacity); } // If there are elements in the current collection, the index of the element to be inserted is directly returned (size+1) // e.g. // There are 3 elements in the current set. At this time, size=3, and the returned value is 3 + 1 = 4 return minCapacity; } ==================================================================================== private void ensureExplicitCapacity(int minCapacity) { // If the current set capacity is sufficient, you do not need to expand it, just modify the value // This value counts the number of times the structure of the current set has been modified, mainly for iterators modCount++; // overflow-conscious code // If the number of position subscripts inserted by the current element is greater than the length of the current set, you need to expand the set if (minCapacity - elementData.length > 0) grow(minCapacity); } =================================================================================== /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * minCapacity:Minimum capacity required * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // New value = current set length + current set length / 2 // e.g. // If the current set length is 10, then the expanded length is: 10 + 10 / 2 = 15 // 15 + 15 / 2 = 15 + 7 = 21 int newCapacity = oldCapacity + (oldCapacity >> 1); // If the length after expansion is not enough for the minimum demand capacity, the minimum capacity demand value is directly used as the value after expansion if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // If the latter value is greater than the Integer maximum value of - 8, then huge capacity is used if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: // In fact, it is the operation of re creating an ArrayList with the capacity of the expanded capacity and copying the original elements to the new collection elementData = Arrays.copyOf(elementData, newCapacity); } ================================================================================== /** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; ================================================================================== private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
Java middle operators supplement
In some algorithms, we often see the writing method of num > > > 1, which is equivalent to num divided by 2. Why not write num divided by 2 directly? Because the data in the computer is stored in the form of binary, the bottom layer of addition, subtraction, multiplication and division of mathematical operation is also realized by binary shift, which is more direct than the mathematical operation. Look at the shift operators in Java. There are three types: < 1: shift left operator, Num < 1, equivalent to num multiplied by 2 >>: shift right operator, Num > > 1, equivalent to num divided by 2 >>>: unsigned shift right operator, Num > > > 1, which is equivalent to num divided by 2, ignoring the sign bits, and filling the empty bits with 0 Example: int num = 8; int num1 = num << 1; //num1 = 16 int num2 = num >> 1 ; //num2 = 4 int num3 = num >>> 1; //num3 = 4 Note: the unsigned right shift operator ignores the sign bit of the highest bit, 0 complements the highest bit, and the unsigned right shift operator > > > is only meaningful for 32-bit and 64 bit values. There are only three kinds of displacements in Java. There is no left unsigned displacement. int num = -8; int num1 = num << 1; //num1 = -16 int num2 = num >> 1 ; //num2 = -4 int num3 = num >>> 1; //num3 = 21 4748 3644 Why is num3 the number of 21 4748 3644? Because the value is stored in the form of complement in the computer, the complement of - 8 is [11111111111111111111111111111111000], and moving one bit to the right becomes [01111111111111111111111111111111100], the highest 1 represents a negative number, the highest 0 represents a positive number, when > > 0 complements the highest sign bit, and the decimal system of [01111111111111111111111111111100] is 21 4748 3644. In the case of positive displacement, > > and > > > are the same, while in the case of negative displacement, they are different.
3. add() of ArrayList
/** * Inserts the specified element at the specified position in this * list. Shifts the element currently at that position (if any) and * any subsequent elements to the right (adds one to their indices). * * @param index index at which the specified element is to be inserted * @param element element to be inserted * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { // Scope check: whether the inserted element position exceeds the current container capacity boundary // eg: if the current set capacity is 10 and the number of elements is 10, then size=10, the index can only be 0-9, and the automatic container expansion will be completed in the next step rangeCheckForAdd(index); // The algorithm is consistent by appending elements ensureCapacityInternal(size + 1); // Increments modCount!! // Move the size index elements after the position you want to insert back System.arraycopy(elementData, index, elementData, index + 1, size - index); // Insert element into specified location elementData[index] = element; size++; } =================================================================================== /** * A version of rangeCheck used by add and addAll. */ private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
4. remove() of ArrayList
/** * Removes the element at the specified position in this list. * Shifts any subsequent elements to the left (subtracts one from their * indices). * * @param index the index of the element to be removed * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { // Verify that the incoming corner is within the scope of the current collection rangeCheck(index); // Increase the number of set structure modifications modCount++; // Element value to delete E oldValue = elementData(index); // Number of elements to be moved int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); // Let GC clear elementData[--size] = null; // clear to let GC do its work // Return deleted elements return oldValue; }
5. remove() of ArrayList
/** * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index * <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> * (if such an element exists). Returns <tt>true</tt> if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present * @return <tt>true</tt> if this list contained the specified element */ public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) // If an element is null if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } =================================================================== /* * Private remove method that skips bounds checking and does not * return the value removed. */ // This method does not perform boundary checking private void fastRemove(int index) { // Accumulated structure modification times 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 }
6. clear() of ArrayList
It can be seen that the method is to set all elements to null
/** * Removes all of the elements from this list. The list will * be empty after this call returns. */ public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }