Learn more about ArrayList s in Java collections

Keywords: Java

What is an ArrayList

ArrayList is an expandable array Resizable-array that implements all the methods of the List interface.

Here are a few points from a simple description of ArrayList

  1. ArrayList is an array, but unlike general arrays, it is scalable and has a fixed capacity.
  2. ArrayList implements the List interface, so ArrayList implements the basic methods that List contains (add, delete, insert, and so on).

10 things ArrayList needs to know

1.ArrayList is implemented internally through arrays

The internal implementation of ArrayList is through an array of objects
transient Object[] elementData

2. The execution time of the ArrayList add operation is fixed

The ArrayList add() method essentially implements adding an element elementData[size++] = el at the end of the array, so the time complexity of execution is fixed O(1), and adding n elements is O(n)

3. In addition to the add method, other operations such as insertion and deletion are linear.

Because it is essentially an operation on an array, when inserting or deleting data in the array, the array needs to be shifted first. When inserting, the data after the insertion point needs to be moved all backwards. When deleting, the data after the deletion node needs to be moved all forward with O(n) operation time complexity.

4. Thread security issues

ArrayList is thread insecure when threads modify it in a multithreaded situation.Thread security issues depend on how they are applied.

5. About capacity expansion

ArrayList has its own automatic scaling mechanism and can also be scaled up manually when data size is expected to be processed

1. Automatic capacity expansion

When an ArrayList adds an element (add, addAll operation), it first detects if the current internal array elementData[] is large enough to add a new element, or if it is not, it expands. ArrayList has a maximum capacity of Integer.MAX_VALUE. The flow of automatic expansion calls is as follows

public boolean add(E e) {
    // Automatically expands and records the number of element modifications, mainly for concurrent modification errors
    ensureCapacityInternal(size + 1); 
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// Returns the larger of an array size value of minCapacity and the default DEFAULT_CAPACITY
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // If it is an empty array, it is created by a new ArrayList()
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
      // Default size is DEFAULT_CAPACITY = 10
      return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}


Actual expanded capacity segment

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // Compare the size returned by the above method with the current elementData size to determine if an extension is required
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}
private void grow(int minCapacity) { // Specific expansion code
    int oldCapacity = elementData.length;
    // Capacity always increases by about 1.5 times after capacity expansion by 0.5 times before capacity expansion
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
    // If the maximum number limit is exceeded after expansion, it is handled by hugeCapacity()
    if (newCapacity - MAX_ARRAY_SIZE > 0)
          newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
          throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

2. Manually expand capacity or specify initial capacity

Manual expansion of ArrayList mainly calls the ensureCapacity(int minCapacity) method, in addition to specifying the initial capacity when creating an ArrayList

Create a new ArrayList <> (initialCapacity) with its implementation specified directly

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // Create an array of the specified capacity
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

Enlargement during processing by the ensure Capacity (int minCapacity) method generally occurs when the initial capacity is found to be inadequate for the current data requirements during processing, and to avoid waste of resources during automatic expansion, since an array copy occurs each time an automatic expansion occurs.

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0: DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

3. Finally, look at the array replication at capacity expansion, which is done using the Arrays.copyOf(origin,newLenght) method

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}
// Specific Copy Code
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    // Copy data from old arrays to new arrays
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

6. Questions about how ArrayList is passed.

When an ArrayList is passed in a method, it passes a reference to an object. When a method modifies an ArrayList, the modification is reflected in all the places that reference the ArrayList. No matter how many passes, it refers to data in the same memory area. Therefore, if you want to ensure that the content remains unchanged during the transfer, you should clone one copy of the (using the clone () method of the ArrayList) for transfer (It is important to note that the Collections.copy(dest,src) method can be used for deep cloning, depending on the needs.

7. The relationship between capacity and size of ArrayList

If you're looking for a relationship between them, it might be size = number of elements <= capacity = capacity size.Size reflects how much data is stored in the current array, while capacity reflects the capacity of an array inside ArrayList.It is not customary to think of capacity as both size

8. Selectivity of ArrayList and LinkedList

Why are ArrayList s and LinkedList s selective because they have different performance when get ting, add ing, removing?

ArrayList is an internal implementation that is an array, and the time complexity when accessing randomly is O(1) knowing that the index is immediately accessible, while its insertion and deletion operations are relatively cumbersome and require linear time complexity for shift operations.

LinkedList is a two-way joined table Doubly-linked, with O(1) time complexity for insertion and deletion, but the index (taking the value on the index bit) needs to be traversed from the header or tail.

So which one you choose depends entirely on whether you want to do random access or add or delete more elements.

9. Why elementData uses transient s to decorate it

The transient s keyword prevents object serialization, so why do you want to prevent elementData serialization? That's because elementData is an array, and it doesn't hold values at every location in the array. There may only be 10 objects in an array with a capacity of 10,000.Therefore, it cannot be serialized, and the methods of writeObject and readObject are overridden in ArrayList to control the serialization of ArrayList.

10. What is the use of ArrayList to implement the RandomAccess interface

The RandomAccess interface is a markup interface and does not define any methods. ArrayList implements it as a markup ArrayList that supports the fast random access feature!

Not Ended to Continue

Posted by geus on Fri, 26 Apr 2019 12:18:35 -0700