每日源码之ArrayList自动扩容实现(add单个元素VS集合)20220526

动态增长原理:

首先看一下往ArrayList加对象的方法add&addAll():

  • add():
 /**
     * Appends the specified element to the end of this list.
     添加到list的最后面
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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++;
    }
  • addAll():
/**
添加到最后面
*/  
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;  //确定增加的对象的长度
        ensureCapacityInternal(size + numNew);  // Increments modCount  当前list和增加对象的list,进行动态扩容
        System.arraycopy(a, 0, elementData, size, numNew); 
       //复制
        size += numNew;
        return numNew != 0;
    }

/**
在索引位置插入
*/
 public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount 先扩容
      
     //开始移动
        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

从上述方法可以看出,add之后,如果是单个元素,size+1 入参到 ensureCapacityInternal()中,如果是一个集合类型,会将其转化为Object[]确认其长度,再size+numNew入参到ensureCapacityInternal()中。

  • ensureCapacityInternal()方法:
//确认内部容积
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

调用了2个方法,ensureExplicitCapacity(int),入参为calculateCapacity(elementData, minCapacity)

  • calculateCapacity(elementData, minCapacity)方法:这是一个静态方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

计算容积,如果是空的(第一次),则返回默认容积10 和最小容积的最大值,其实就是10或者更大的,

如果不是第一次添加,那直接就返回最后要加的值。

  • ensureExplicitCapacity(int minCapacity)方法:
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
    //
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

可以知道这是确认清楚容量,如果计算的min容量和目前所装的长度差大于0,则说明需要扩容。

  • 增长:提高容量以便至少满足最少 minCapacity 容量!!
  /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *通过min容量参数,增大容量以确保他能至少装下元素
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; //原来的长度
        int newCapacity = oldCapacity + (oldCapacity >> 1); //新长度 扩容1.5倍
        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);
        //最后复制,新数组,结束
    }

这个方法就是在增大容量,扩1.5倍如果不够就直接变到指定的容量,然后判断是否超过了数组最大容量,超过就调用调用hugeCapacity方法。

这个方法首先计算出一个容量,大小为oldCapacity + (oldCapacity >> 1)。即elementData数组长度的1.5倍。再从minCapacity 和

这个容量中取较大的值作为扩容后的新的数组的大小。

  • MAX_ARRAY_SIZE参数:
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit即有些虚拟机会在数组中保存 header words 头部字。
     */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //防止内存溢出

即有些虚拟机会在数组中保存 header words 头部字。

  • hugeCapacity()方法:巨大容量
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

因为数组理论上长度就是 Integer.MAX_VALUE 个别JVM 设计上的问题 咱们可以尽量照顾下 但并不是说一定因为个别JVM 就一定不让扩容到 整数最大值长度。

如果再满了 那么对不起 直接到将数组长度设置为整数最大值, 爱咋咋地!

要分配的数组的最大大小(除非必要)。 有些虚拟机在数组中保留一些头词。 尝试分配更大的数组可能导致 OutOfMemoryError:请求的数组大小超过虚拟机限制

因此,数组最大容量是 Integer.MAX_VALUE . ,在图示情况扩容到 MAX_ARRAY_SIZE 是为了扩容到 MAX_ARRAY_SIZE以上长度就OOM的虚拟机可以尽量不OOM,如果还放不下没办法,对不起了大兄弟!

通过分析可以发现,ArrayList的扩容会产生一个新的数组,将原来数组的值复制到新的数组中。会消耗一定的资源。所以我们初始化ArrayList时,最好可以估算一个初始的大小。

https://stackoverflow.com/questions/35756277/why-the-maximum-array-size-of-arraylist-is-integer-max-value-8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值