Arraylist扩容方式的深入个人理解

			**Arraylist扩容方式的深入个人理解**

在这里插入图片描述

Arraylist扩容方式的深入个人理解

概述

ArrayList 是 基于Object数组实现的一种动态数组集合。当向 ArrayList 添加元素时,如果当前数组已满,就需要对数组进行扩容。扩容的过程是这样进行的:

创建一个新的数组,即新数组大小为原数组大小的 1.5 倍。

int newCapacity = oldCapacity + (oldCapacity >> 1);

将原数组中的所有元素复制到新数组中并,将将新元素添加到新数组中。并将新数组替换原数组,原数组成为垃圾对象,在垃圾回收时会被回收。

这种扩容方式的时间复杂度为 O(n),其中 n 为数组中的元素数量。因为需要将所有元素复制到新数组中,所以扩容操作的开销比较大。

为了减少扩容操作的次数,ArrayList 通常会采用一种“预留空间”的策略。当 ArrayList 创建时,会为数组预留一定的空间,采用ArrayList的有参构造创建对象,这样在添加元素时,只有当数组大小达到预留空间时才进行扩容操作。

可以看出,当 ArrayList 需要扩容时,它需要分配新的数组并将元素复制到新数组中。这个过程会涉及到数组分配、复制和垃圾回收等操作,因此可能非常昂贵。为了避免频繁的扩容操作,ArrayList 通常会在初始化时为数组预留一些空间。我们可以通过构造函数中的初始容量参数来设置预留空间的大小,以此来控制扩容的频率和代价。

总的来说,ArrayList 的扩容方式虽然时间复杂度比较高,但是它提供了一种高效的动态数组实现方式,适用于需要频繁进行查询读取数据等操作的场景。在实际应用中,我们需要根据具体的场景和性能要求来合理使用 ArrayList 并设置预留空间大小。

例如,如果我们知道 ArrayList 中最多可能会存储 1000 个元素,我们可以在初始化时设置 ArrayList 的容量为 1000,这样就可以避免多次扩容操作,提高性能。

源码角度分析扩容

从源码的角度来看,ArrayList 扩容的实现主要依赖于 ensureCapacityInternal 和 grow 两个方法。让我们来看一下这两个方法的实现细节。

ensureCapacityInternal(int minCapacity)

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

该方法首先判断 elementData 是否为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA

// DEFAULTCAPACITY_EMPTY_ELEMENTDATA常量对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  

如果采用的是无参构造方法创建的ArrayList对象,第一次添加元素时,会将minCapacity设置为DEFAULT_CAPACITY,

 // DEFAULT_CAPACITY常量对象
private static final int DEFAULT_CAPACITY = 10;

常量DEFAULT_CAPACITY=10;则将 minCapacity 设置为 DEFAULT_CAPACITY 和 minCapacity 之间的最大值,即初始化时的默认容量。

// 首先判断elementData对象是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则将 minCapacity 设置为 DEFAULT_CAPACITY 和 minCapacity 之间的最大值,即初始化时的默认容量。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }

然后调用 ensureExplicitCapacity 方法,该方法会进行实际的扩容操作。

grow(int minCapacity)

    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);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

该方法首先计算新的容量 newCapacity,该值为原容量的 1.5 倍。然后根据 minCapacity 和 MAX_ARRAY_SIZE 的值,对 newCapacity 进行调整。最后使用 Arrays.copyOf 方法将原数组复制到新数组中,完成扩容操作。

需要注意的是,ArrayList 的最大容量为 Integer.MAX_VALUE,也就是说,当需要扩容的容量超过该值时,会抛出 OutOfMemoryError 异常。

小结

综上所述,ArrayList 扩容的实现主要是通过 ensureCapacityInternal 和 grow 方法来实现的。在 ensureCapacityInternal 方法中,会先判断 elementData 是否为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,如果是,则将 minCapacity 设置为DEFAULT_CAPACITY 和 minCapacity 之间的最大值,即初始化时的默认容量。然后调用 ensureExplicitCapacity 方法,该方法会进行实际的扩容操作。在 grow 方法中,会计算出新的容量 newCapacity,然后根据 minCapacity 和 MAX_ARRAY_SIZE 的值,对 newCapacity 进行调整。最后使用 Arrays.copyOf 方法将原数组复制到新数组中,完成扩容操作。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值