Let's talk about ArrayList source code (based on JDK1.8)

I have been working for almost a year, and I plan to study the source code of JDK in the near future, so there is a series of deadly java series.

  • ArrayList is an array queue, equivalent to a dynamic array. Compared with the array in Java, its capacity can grow dynamically. It inherits from AbstractList and implements the interfaces of List, RandomAccess, Cloneable, java.io.Serializable.
  • ArrayList inherits AbstractList and implements List. It is an array queue that provides related functions such as adding, deleting, modifying, and traversing.
  • ArrayList implements the RandmoAccess interface, which provides random access. RandmoAccess is implemented by List in java and provides quick access to List. In ArrayList, we can quickly get the element object by the serial number of the element; this is fast random access.
  • ArrayList implements the Cloneable interface, that is, it overrides the function clone() and can be cloned.
  • ArrayList implements the java.io.Serializable interface, which means that ArrayList supports serialization and can be transmitted through serialization, including network transmission and local file serialization.
  • Unlike Vector, operations in ArrayList are not thread-safe! Therefore, it is recommended to use ArrayList in a single thread, and it is recommended to choose CopyOnWriteArrayList in multi-threading. Of course, Vector can also be used, but it is not recommended.

Advertise, the owner made his own wheels, if you are interested, please click [github]: https://github.com/haifeiWu/lightconf

UML diagram of ArrayList

UML diagram of ArrayList

Member variables of ArrayList and their meanings

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * ArrayList默认初始大小为10.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 默认的空值数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 当ArrayList不传参数时,elementData默认初始化成DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 实现ArrayList的object数组<br><br/>
     * transient关键字扫盲,在实现Serilizable接口后,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * ArrayList的大小
     */
    private int size;

    /**
     * 构造一个初始容量的数组
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * 初始化一个size为10的空值ArrayList.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 按照集合迭代器返回的顺序构造包含指定集合元素的列表。
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
}

Talk about the main method implementation of ArrayList


    /**
     * 获取ArrayList对应下标的元素,时间复杂度为O(1).
     */
    public E get(int index) {
        // 检查数组下标越界问题
        rangeCheck(index);

        return elementData(index);
    }

    /**
     * 将对应下标的元素更新成现在的值
     */
    public E set(int index, E element) {
        // 检查数组下标越界问题
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    /**
     * ArrayList中追加新值.
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     * 在ArrayList的指定下标下添加值,时间复杂度为O(n)
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

    /**
     * 移除指定下标的值,时间复杂度为O(1)。
     */
    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;
    }

    /**
     * 移除指定值,时间复杂度为O(n)。
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                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;
    }

    /**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */
    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);
        }
    }

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

    /**
     * ArrayList的最大长度是2147483639
     * 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;

    /**
     * 用于ArrayList扩容的方法,扩容的策略是int newCapacity = oldCapacity + (oldCapacity >> 1),即每次扩容是原来长度的1.5倍
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

Hands-on implementation of ArrayList

Because the code is too long, it will not be posted here. Interested partners can go to my github to view [github]: https://github.com/haifeiWu/interview-collect/tree/master/src/main /java/com/haifeiwu/interview/structure/list

summary

Regarding the source code of ArrayList, some important summaries are given:

1. Note its three different construction methods. The default capacity of the ArrayList constructed by the no-parameter constructor is 10. The constructor with the Collection parameter converts the Collection into an array and assigns it to the implementation array elementData of the ArrayList.

2. Pay attention to the way to expand the capacity ensureCapacity. Each time an ArrayList adds an element (perhaps 1 or a group), this method must be called to ensure sufficient capacity. When the capacity is not enough to accommodate the current number of elements, the new capacity is set to 1.5 times the old capacity. If the new capacity after setting is not enough, the new capacity is directly set to the incoming parameter (that is, the required capacity), and then use the Arrays.copyof() method to copy the elements to the new array (see point 3 below). It can be seen from this that when the capacity is not enough, each time an element is added, the original element must be copied to a new array, which is very time-consuming. Therefore, it is recommended to use ArrayList when the number of elements can be determined in advance.

3. The Arrays.copyof() and System.arraycopy() methods are called a lot in the implementation of ArrayList. We must take a look at the code implementation of these two methods.

 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

Obviously, another copyof method is called. This method has three parameters. The last parameter specifies the type of data to be converted. The source code is as follows:


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);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

As can be seen from the code, the copyOf method actually creates an array with a length of newlength inside it, and then calls the System.arraycopy() method to copy the elements in the original array to the new array.
Let's look at the System.arraycopy() method. This method is marked as native and calls the C/C++ code of the system, which cannot be seen in the JDK, so I won’t do a more in-depth understanding here, but according to the usual practice of the JDK, use the C/C++ code of the system It is definitely no problem to copy the array efficiency.

4. ArrayList is implemented based on arrays, and the element at the specified position can be directly searched through the subscript index, so the search efficiency is high, but each time an element is inserted or deleted, a large number of elements must be moved, and the efficiency of inserting and deleting elements is low.

5. In the method of finding the index value of a given element, the source code divides the value of the element into two cases: null and not null, so it seems that the element is allowed to be null in ArrayList.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325949603&siteId=291194637