(三)ArrayList的扩容机制

扩容机制(无参)

10个元素以内的容器是怎么变化的?

1.继上述代码进行分析.我们需要搞清楚elementData.length和元素个数之间的关系
elementData.length是这个容器的大小.元素个数就是size;所以咱们回到add方法重新走一遍

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 这个传递过去的是元素添加够的大小.但是实际上还没有添加
    elementData[size++] = e; //这行代码才是添加元素的
    return true;
}

2.进入ensureCapacityInternal();

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

3.先看看里面的calculateCapacity是做什么的,这个是计算容量的.
这里是使用的无参构造方法创建的ArrayList容器.那么就返回一个容器的默认大小.上边有这个DEFAULT_CAPACITY值为10.

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

4.回到2.进入ensureExplicitCapacity()

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;    //这个网上说是一个快速报错机制,可能我们看的是同一篇文章

    // overflow-conscious code
    if (minCapacity - elementData.length > 0) 
//这里是扩容的条件.如果元素的个数大于数组的长度,就进行扩容.
//当我们第一add的时候会进行扩容.上面分析过了,会直接扩容到10.但是等我们第二次一直到第10次,都不会进行扩容.
//只有第11次的时候,才会进行扩容因为11 -  10 > 0.现在minCapacity为11
        grow(minCapacity);
}

添加第11个元素的时候发生了什么?

可以看一下上面的文字,只有我们的元素要为11的时候才会进行扩容.扩容1.5.倍.到15个大小

5.进入这个grow方法,扩容的核心方法,可以看到扩容并不是在我们要添加的元素的个数的基础上扩容的,而是在容器elementData的长度.

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

看这个代码,oldCapacity现在是10 ,右移1位,相当于除以2.就是5.再加上原来的10.所以newCapacity为15
基础上进行1.5倍的扩容,调用了Arrays.copyOf在原有的基础上返回了一个新的elementsData;
第一个if是扩容的最小值10,也就是我们第一次调用add的时候会走进去的if.

容器要扩容到Integer.MAX_VALUE +1的时候发生了什么?

第二个if可以看到走进了一个静态方法里面

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

6.看一下这个静态方法,这个里面涉及到了点其他位置的只是,可能是那个快速报错机制.我猜的,以为毕竟不可能无线扩容.
这个数组的最大值是0x7fffffff.整形就是21亿.01111111 11111111 11111111 11111111.大概就是这么个情况.

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 > MAX_ARRAY_SIZE,但是minCapacity < MAX_ARRAY_SIZE的时候,会将newCapacity扩容为MAX_ARRAY_SIZE.
知道我们的minCapacity > MAX_ARRAY_SIZE的时候才会扩容的int类型的最大值0X7fffffff.
直到我们的minCapacity超过了int类型的最大值.第一位会变成符号位:即
11111111 11111111 11111111 11111111
会变成负数.抛出OutOfMemoryError错误.内存溢出错误.

总结

第一次使用add方法的时候会进行第一次扩容,容器大小为10
直到我们的元素添加到了11个,超过了容器大小10.就扩容大小为当前容器的1.5倍(向下取整)(即15 * 1.5 = 22)

    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);

然后依次类推一直到要超出容器的大小的时候进行扩容.扩容1.5倍.
当然不是无限大小了.临近最大值的扩容有3种处理
1.扩容后的数组的长度大于int类型的最大值-8,但是实际长度小于int类型的最大值-8.数组会扩容到int类型的最大值-8
2.扩容后的数组的长度大于int类型的最大值-8,但是实际长度大于int类型的最大值-8并且小于等于int类型最大值.会扩容到int类型的最大值
3.扩容后的数组的长度大于int类型的最大值-8,但是实际长度超出了int类型的最大值,会产生内存溢出错误.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值