Source code analysis --ArrayList

Foreword

In this article we analyze another container class, ArrayList, which implements the List interface, implementation class is List, the underlying implementation is an array, let us analyze in detail below. ps: some time ago because the school laboratory course set up all kinds of things plus a final exam, etc., for a long time did not update the blog ...

1. Overview

Between class diagram


RandomAccess ArrayList class implements the interface, support random access.

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
复制代码
The default size of the array 10, and can be seen in the data storage elementData this array.
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;复制代码

2. Expansion

Use ensureCapacityInternal elements are added () method to ensure sufficient capacity, if not enough, you need to use the grow () method for expansion, the size of the new capacity is oldCapacity + (oldCapacity >> 1), which is 1.5 times the capacity of the old .
To specify the approximate size of the capacity expansion operation when you need to call Arrays.copyOf () to copy the entire original array to a new array, which is traversed copy operation, high cost of operation, it is best to create ArrayList object, reduce operational expansion number of times.

It should be noted that the expansion of the operation is also a thread unsafe operation. This line statement is not an atomic operation, so the problem of cross-border access the array will appear in a multithreaded environment. So ArrayList Like a HashMap is thread safe container .

elementData[size++] = e;复制代码

The following is a new element is inserted into the source code and expansion operations:

//插入新元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //这里会产生数组越界问题,因为下面这行语句并不是一个原子操作
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //新容量为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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);
}复制代码

3. Removing elements

System.arraycopy need to call () behind the index + 1 elements are copied to the index position, the operating time complexity is O (N) , the cost can be seen ArrayList erased elements is very high.

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    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
    return oldValue;
}复制代码

4. fail-fast

About 4.1 fail-fast

fail-fast mechanism java collection (Collection) of one error mechanism. When multiple threads operating on the same set of content, it may generate fail-fast event.
For example: When a thread A iterator to traverse through a collection process, the content if the set is changed by the other thread; then thread A accesses the collection, an exception will be thrown ConcurrentModificationException generate fail-fast event.

fail-fast 4.2 ArrayList in

ArrayList modCount used to record the number of changes in the structure . Operation, or adjust the size of all the changes in the structure of the internal array means add or delete at least one element, set the value of the element just not a change in the structure.

ArrayList or when carrying out a sequence of iterations, etc., you need to compare before and after the operation modCount whether to change , if change can raise ConcurrentModificationException.

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}复制代码

The sequence of

ArrayList array based implementation and expansion of dynamic characteristics, and therefore are not necessarily stored array element is used, there is no need for all serialized.
Save element of the array elementData use transient modification of the default keyword to declare the array will not be serialized.

transient Object[] elementData; // non-private to simplify nested class access复制代码
ArrayList implements writeObject () and readObject () to control the sequence of the array only has to fill that portion of the element content.

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}复制代码
You need to use the serialization writeObject ObjectOutputStream () object to be converted and output byte stream. The writeObject () method has writeObject () in the incoming object when it will go to the object's reflection calls writeObject () to achieve serialization. Deserialization using the ObjectInputStream readObject () method, serialization and deserialization similar principles.

ArrayList list = new ArrayList();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list);复制代码

6. Summary

ArrayList from the source point of view do have to be somewhat simpler than the HashMap some paper analyzes the internal data structure ArrayList with several commonly used methods, such as add (), remove (). It also discussed why a multi-threaded environment is a thread-safe container, if you want to use in a multithreaded environment, consider using Vector CopyOnWriteArrayList or two containers, also introduces the fail-fast in the ArrayList serialization issues of application and ArrayList.

Finally, I also welcome you to discuss with each other, pointing out the shortcomings of this article.


Guess you like

Origin juejin.im/post/5d29d66de51d45108c59a5f0