浅谈ArrayList 扩容源码

80 阅读2分钟
  • ArrayList 源码分析

    • Tips:command + 7 可以显示 ArrayList 的所有结构

image.png

-   ArrayList 的三种构造方法:

    ```
      
      public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
              //当大于 0 时,根据值initialCapacity构造一个数组
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;//空数组
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
       //空参构造方法 传入一个默认容量空数组
       public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
        
       public ArrayList(Collection<? extends E> c) {
            Object[] a = c.toArray();//将集合转化为数组
            if ((size = a.length) != 0) {
                if (c.getClass() == ArrayList.class) {
                    elementData = a;
                } else {
                    elementData = Arrays.copyOf(a, size, Object[].class);
                }
            } else {
                // replace with empty array.
                elementData = EMPTY_ELEMENTDATA;
            }
        }
    ```

-   插入数据两种实现方法

    ```
       public boolean add(E e) {
         //检测是否需要扩容
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
    ​
    ​
        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++;
        }
    ```

-   检测数组是否需要扩容

    ```
    //传入的 minCapacity 为 size + 1,即为当前数组容量 + 1 
    private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    ​
     private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
              //如果以无参构造函数创建 ArrayList,返回默认容量和最小容量中的较大值作为所需容量
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
        // 否则直接返回最小容量
            return minCapacity;
        }
    ​
    //判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
            modCount++;
            //判断当前数组容量是否足以存储minCapacity个元素
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    ```

-   扩容方法

    ```
    /**
     * 要分配的最大数组大小
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    ​
    private void grow(int minCapacity) {
            // oldCapacity为旧容量,newCapacity为新容量
            int oldCapacity = elementData.length;
          // 将oldCapacity 右移一位,其效果相当于oldCapacity /2,
        // 我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
            int newCapacity = oldCapacity + (oldCapacity >> 1);
      // 然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
      // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
        // 如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
    ​
            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);
        }
    ```

    ">>"(移位运算符):>>1 右移一位相当于除 2,右移 n 位相当于除以 2 的 n 次方。
    这里 oldCapacity 明显右移了 1 位所以相当于 oldCapacity /2。
    对于大数据的 2 进制运算,位移运算符比那些普通运算符的运算要快很多,
    因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源