【2021-07-02】了解 ArrayList 的扩容机制吗?扩容因子是多少?为什么?
请移步至:
每日一题 查看更多的题目 ~
答:
我们来看下 ArrayList 的 add 方法:
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);
}每次向 ArrayList 中添加一个元素时,都会先执行 ensureCapacityInternal 方法,确保 ArrayList 底层数组 elementData 的容量是够用的,如果 elementData 容量不为空,则执行 ensureExplicitCapacity 方法。
ensureExplicitCapacity 方法中进行判断:如果当前 ArrayList 的 size + 1 大于 elementData 的容量大小,则执行扩容方法 grow
grow
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);
}我们看到,在 grow 方法中,声明了新容量的大小 newCapacity:
int newCapacity = oldCapacity + (oldCapacity >> 1);其大小为旧容量(oldCapacity)的 1.5 倍,而扩容的操作实质是开辟了新的数组空间,使用 Arrays.copyOf 方法将旧数组的所有元素写入到新的数组中。
所以,ArrayList 的扩容因子为 1.5。
那么,为何扩容因子被设计成 1.5 倍呢?
假定,我们每次只向 ArrayList 中添加一个元素,如果此时 ArrayList 底层数组已满就使用增长因子 k 来进行扩容。
如果给定一个大小为 n 的数组,扩容所需要的开销为 
那么,插入  个元素所需的开销就是:
 个元素所需的开销就是:

添加每个元素的均摊开销为:

对其求导得:

所以,当  时,
 时, ,此时
,此时  有最小值。
 有最小值。
通过上述的推导,我们得到结论,扩容因子如果被设计成 2 可能是最优的,但是这样做仍然有缺陷,使用 2 作为扩容因子的问题在于,每次扩展的新尺寸必然刚好大于之前分配的总和,也就是说,之前分配的内存空间不可能被使用。这样对缓存是不友好的,最好将扩容因子设置为 
以初始容量为 4 作为示例,我们将扩容因子 k 设为 2 和 1.5 进行内存可用性的对比:

因为数组的扩容本质就是,不断开辟比原数组更大的连续内存空间,当扩容因子 k 设置为 2 时,我们发现,每次新开辟的数组都比之前分配的内存空间总和要大一些,这样导致之前的内存空间不可复用。而在 k 设置为 1.5 时,我们发现,之前的内存空间可以得到复用,至于这个数字为何是 1.5,原因在于:
int newCapacity = oldCapacity + (oldCapacity >> 1);1.5 可以通过位运算得出,位运算可以优化计算性能。
 
           每日一题
每日一题 
        
         
             
             关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号