Collection source code analysis: the underlying implementation principle of ArrayList

jdk7.0 source code analysis ArrayList underlying implementation principle


1. Overview of ArrayList

ArrayList is the implementation class of List. It stores multiple elements, ordered, subscripted, and elements can be repeated, allowing null to be stored as elements; thread is not safe because the underlying method is asynchronous.

Second, the storage structure of ArrayList

The bottom layer is implemented by an array, so each ArrayList instance has a capacity, which represents the length of the bottom array. The default length is 10. As the stored data continues to increase, the underlying array needs to be expanded. When expanding, the data in the original array needs to be copied to the new array. Therefore, if you can predict the number, you can specify the capacity when creating the ArrayList object. If you store a large amount at once When the element of, you can call the ensureCapacity(int minCapacity) method to increase the capacity of the ArrayList, thereby reducing the number of expansions.

[External link image transfer failed. The source site may have an anti-hotlinking mechanism. It is recommended to save the image and upload it directly (img-C1z7S1B1-1586450232815) (C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\ 1572847414456.png)]

Three, ArrayList internal implementation principle mechanism (source code analysis)

  1. The basic elements of ArrayList

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, 
                        java.io.Serializable
    {
          
          
        private static final long serialVersionUID = 8683452581122892189L;
    
    	// 默认的容量 为 10
        private static final int DEFAULT_CAPACITY = 10;
    	
    	// 用于共享的空实例数组对象
        private static final Object[] EMPTY_ELEMENTDATA = {
          
          };
    
        // 用于存储ArrayList元素的底层数组
        private transient Object[] elementData;
    	
    	// 记录 ArrayList中元素的个数,同时控制存储的下标
        private int size;
    
  2. The construction method in ArrayList:

    /*
    	无参数的构造方法,采用默认的初始容量为10,
    	如果利用无参数的构造方法创建ArrayList对象,则
    	并没有完成 底层数组的初始化
    */
    public ArrayList() {
          
          
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }
    /*
    	用指定容量完成ArrayList对象的创建:
    	参数代表底层数组的长度,但是如果指定的长度小于0,则抛出异常,
    	如果基于此构造方法创建ArrayList对象,则创建ArrayList对象
    	时,完成底层数组的初始化
    */
    public ArrayList(int initialCapacity) {
          
          
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    /*
    	将Collection集合作为参数构造 ArrayList 对象,
    	ArrayList集合用于 存储Collection集合中的数据
    */
    public ArrayList(Collection<? extends E> c) {
          
          
        elementData = c.toArray();
        size = elementData.length;
        
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, 
                                        Object[].class);
    }
    
  3. Added in ArrayList: implementation of add method

    /*
    	往ArrayList集合中添加一个元素,size统计底层数组中有效元素的个数,
    	同时控制存储的下标
    */
    public boolean add(E e) {
          
          
        // 在 内部确认容量是否够用
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 将 添加元素存储在底层数组相对应的位置
        elementData[size++] = e;
        return true;
    }
    
    // 确认容量
    private void ensureCapacityInternal(int minCapacity) {
          
          
        if (elementData == EMPTY_ELEMENTDATA) {
          
          
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
        ensureExplicitCapacity(minCapacity);
    }
    
    // 根据容量 判断 是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
          
          
        modCount++; // 修改的次数加一
    	// 如果需要的最小容量,大于底层数组的长度,则扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    private void grow(int minCapacity) {
          
          
        // 获取底层数组的原始数组的长度
        int oldCapacity = elementData.length;
        /*
        	>> 代表右移,原数据除2取整数部分
        	新数组长度 = 原数组长度 + 原数组长度/2;
        	即:新数组长度是原数组长度的 1.5 倍左右
        */
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 数组的扩容 并 复制
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    

    Analysis: When the underlying array of ArrayList is expanded, the capacity after each expansion is about 1.5 times the original capacity.

    ​ At the same time, you need to copy the elements in the original array to the new array one by one, through the Arrays.copyOf() method

    ​ The creation of a new array and the assignment of elements, so every expansion, the cost is very high, so we are using

    ​ Avoid expansion as much as possible when using it. If you can predict the number of storage elements when using ArrayList, you can

    ​ When creating an ArrayList object, use a parameterized construction method to specify the capacity, or according to the actual capacity

    ​ The demand calls the ensureCapacity(int minCapacity) method to specify the capacity.

  4. Delete method in ArrayList: remove

    // 根据下标删除对应元素
    public E remove(int index) {
          
          
        // 检测指定下标是否超过 最大下标
        rangeCheck(index);
    	
        //对集合的改变次数 + 1 
        modCount++;
        // 获取 底层数组对应的下标元素
        E oldValue = elementData(index);
    	// 需要移动元素的个数
        int numMoved = size - index - 1;
        // 被删除下标开始,后一个元素覆盖前一个元素
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 最后一个元素置 null,同时元素个数 - 1
        elementData[--size] = null; // clear to let GC do its work
    	// 将被删除的元素作为返回值返回
        return oldValue;
    }
    
    // 删除指定的元素
    public boolean remove(Object o) {
          
          
         // 如果 被删除的元素为null
         if (o == null) {
          
          
             // 遍历数组,删除第一个为null的元素
             for (int index = 0; index < size; index++)
                 if (elementData[index] == null) {
          
          
                     fastRemove(index);
                     return true;
                 }
         } else {
          
          
             /* 如果被删除的元素不为null,则遍历输出,删除第一个
                和指定数据相同的元素
             */
             for (int index = 0; index < size; index++)
                 if (o.equals(elementData[index])) {
          
          
                     fastRemove(index);
                     return true;
                 }
         }
         return false;
     }
    
  5. **Add in ArrayList: Implementation of get method**

    // 根据指定下标获取对应集合中的元素
    public E get(int index) {
          
          
        // 检测指定下标是否超过 最大下标
        rangeCheck(index);
    	// 调用 方法,获取底层数组对应的下标元素
        return elementData(index);
    }
    
  6. **Add in ArrayList: Implementation of get method**

// 修改指定下标对应的数组元素
public E set(int index, E element) {
    
    
    // 检测指定下标是否超过 最大下标
    rangeCheck(index);
    
	// 获取 底层数组对应的下标元素
    E oldValue = elementData(index);
    // 将新元素存储在 对应下标位置上
    elementData[index] = element;
    return oldValue;//被修改的数据作为返回值返回
}

Four, Fail-Fast mechanism (fast failure)

  1. In the process of using iterators to traverse the collection, modifying the content of the objects in the collection (addition, deletion, modification) will throw java.util.ConcurrentModificationException (concurrent modification exception), which is the so-called Fail-Fast strategy.

  2. Implementation principle: The modCount property is defined in the parent class of ArrayList, which records the number of modifications to ArrayList. As long as the content in ArrayList is modified, modCount will increase; when the iterator is initialized, the value of modCount will be assigned to expectedModCount In each iteration (nextNode operation), it will first determine whether
    the values ​​of modCount and expectedModCount are equal. If they are not equal, it means that the ArrayList collection is operated, and a java.util.ConcurrentModificationException will be thrown.
    Note: modCount is declared as volatile to ensure the visibility of changes between threads.

Guess you like

Origin blog.csdn.net/Java_lover_zpark/article/details/105424559