ArrayList的扩容机制

257 阅读4分钟

ArrayList 扩容的本质其实是计算出新的扩容数组的 size 后实例化,并将原有的数组内容复制到新的数组中去。

下面附上一些重要的代码:

public class ArrayList<E> extends AbstractList<E> implements List<E>,
RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;
    
    /**
    * 默认初始容量大小
    */
    private static final int DEFAULT_CAPACITY = 10;
    
    /**
    * 空数组
    */
    private static final Object[] EMPTY_ELEMENTDATA = {  };
    
    //用于默认大小空实例的共享空数组实例。
    //我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {  };
    
    /**
    * 要分配的最大数组大小
    */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    /**
    * 保存ArrayList数据的数组
    */
    transient Object[] elementData;
    
    /**
    * 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);
        }
    }
    
    /**
    *默认无参构造函数
    */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    /**
    * 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
    */
    public ArrayList(Collection<?extends E> c) {
        // 将指定集合转换为数组
        elementData = c.toArray();
        
        // 如果elementData数组的长度不为0
        if ((size = elementData.length) != 0) {
            // 如果elementData不是Object类型数据(c.toArray可能返回的不是Object类型的数组所以加上下面的语句用于判断)
            if (elementData.getClass() != Object[].class) {
                // 将原来不是Object类型的elementData数组的内容,赋值给新的Object类型的elementData数组
                elementData = Arrays.copyOf(elementData, size, Object[].class);
            }
        } else {
            // 其他情况,用空数组代替
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  
  
    /**
    * 将指定的元素追加到此列表的末尾
    */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        // 这里看到ArrayList添加元素的实质就相当于为数组赋值
        elementData[size++] = e;
        return true;
    }
​
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  
    // 得到最小扩容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 取得默认容量与传入参数之间的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    // 判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    
        if ((minCapacity - elementData.length) > 0) {
            // 调用grow方法进行扩容
            grow(minCapacity);
        }
    }
    
    /**
    * ArrayList扩容的核心方法
    */
    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;
        }
        
        // 再检查新容量是否超出了ArrayList所定义的最大容量,
        // 若超出了,则调用hugeCapacity()来比较minCapacity和MAX_ARRAY_SIZE,
        // 如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
        if ((newCapacity - MAX_ARRAY_SIZE) > 0) {
            newCapacity = hugeCapacity(minCapacity);
        }
        
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    // 比较minCapacity和 MAX_ARRAY_SIZE
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) {
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }
   
}

在调用 add 方法时,会判断是否可以容纳元素,若能,则直接添加在末尾;若不能,则进行扩容,然后再把元素添加在末尾。判断是否可以容纳元素是通过 ensureCapacityInternal 方法确保当前 ArrayList 维护的数组具有存储新元素的能力,ensureCapacityInternal 方法内部会调用 ensureExplicitCapacity 方法 ,ensureExplicitCapacity 方法会调用 calculateCapacity 方法,并将其返回值作为参数使用,那我们先说说 calculateCapacity 方法,在我们第一次扩容的时候会判断,如果传入的是一个空数组则最小容量取默认容量与 minCapacity 之间的最大值并返回,我们再回到 ensureExplicitCapacity 方法,ensureExplicitCapacity 方法会拿着刚才返回的 minCapacity 进行判断,若 ArrayList 已有的存储能力满足最低存储要求,则返回 add 直接添加元素,如果最低要求的存储能力大于 ArrayList 已有的存储能力,这就表示 ArrayList 的存储能力不足,因此需要调用 grow 方法进行扩容。grow 首先会获取旧的容量 oldCapacity,然后将 oldCapacity 右移一位并和旧容量相加得到新的容量,其最终的效果就是将新的容量更新为旧容量的 1.5 倍,接着检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,如果新容量大于默认的最大值,就检查是否溢出,最后调用 Arrays.copyOf 方法将 elementData 数组指向新的内存空间并将 elementData 的数据复制到新的内存空间。