为什么要复制ArrayList扩容?有何作用?

2020-05-07 17:42:41 java常见问答 8908

其实有关list的问题有很多,可能常常会被问及就少不了ArrayList扩容相关问题了,那么你知道为什么要复制ArrayList扩容吗?这样有什么作用呢?

我们都知道 ArrayList是List接口的实现类,它其实是支持根据需要而动态增长的数组。java中标准数组是固定长度的,就是说在数组被创建之后,它们就不能被加长或缩短了。这就意味着说在创建数组的时候我们需要知道数组的所需长度,但是有时我们需要动态程序中获取数组长度。ArrayList就是因此而存在的。

所以,了解它的扩容机制对使用它尤为重要。

ArrayList扩容发生在add()方法调用的时候,下面是add()方法的源码示例:

public boolean add(E e)
{
    //扩容
    ensureCapacityInternal(size + 1); // Increments modCount!!
    elementData[size++] = e;
    return true;
}
这就可以看出ensureCapacityInternal() 是用来扩容的, 形参为最小扩容量, 进入此方法后如下:
private void ensureCapacityInternal(int minCapacity)
{
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

通过方法calculateCapacity(elementData, minCapacity)获取:

private static int calculateCapacity(Object[] elementData, int minCapacity)
{
    //如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
    {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

ensureExplicitCapacity方法可以判断是否需要扩容:

private void ensureExplicitCapacity(int minCapacity)
{
    modCount++;
    // 如果最小需要空间比elementData的内存空间要大,则需要扩容
    if (minCapacity - elementData.length > 0)
        //扩容
        grow(minCapacity);
}

重点来了,ArrayList扩容的关键方法grow():

private void grow(int minCapacity)
{
    // 获取到ArrayList中elementData数组的内存空间长度
    int oldCapacity = elementData.length;
    // 扩容至原来的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,
    // 不够就将数组长度设置为需要的长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //若预设值大于默认的最大值检查是否溢出
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
    // 并将elementData的数据复制到新的内存空间
    elementData = Arrays.copyOf(elementData, newCapacity);
}

从该方法中我们可以清晰的看出其实ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并且将原有数组内容复制到新数组中去。

到此扩容就基本完成了。

好了,以上就有关ArrayList扩容的所有内容了,还想了解更多java常见问答知识,记得马上来关注本站相关信息。

推荐阅读:

Linkedlist是每次遍历都从第一个吗?有多少种遍历方式?

mybatis一对多是什么?有何作用?

mybatis批量插入如何实现?有几种方式?