关于ArrayList中扩容的处理

在add方法中,有一段注释为
// overflow-conscious code
即考虑了溢出情况,那么究竟是为何溢出,以及他是如何处理的呢?仔细阅读源码,了解处理的逻辑

add()的基本逻辑,1.判断是否要扩容 2.添加进数组
了解几个基本概念
集合数组的长度 elementData.length
集合元素的个数 size
待添加的元素长度(1),待添加的集合长度(num
需求的最小扩容长度minCapacity = size + 1minCapacity = size + num
以上概念在add(E e) addAll(Collection<? extends E> c)中都有体现

public boolean add(E e) {
	//单个元素size + 1
    ensureCapacityInternal(size + 1);  // Increments modCount!! 
    elementData[size++] = e;
    return true;
}

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();//将待添加的集合转为数组
    int numNew = a.length;
    // 集合数组size + numNew
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

那么进一步看判断扩容方法,

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

首先内部逻辑里面calculate只是判断本集合是否为空元素集合,如果是空元素集合,返回Math.max(DEFAULT_CAPACITY, minCapacity),即Math.max(10, 扩容长度)
如:通过空参构造器新建一个arraylist,根据构造器定义,此时集合数组长度为0,通过add添加一个string型数据“hello”,则会返回Math.max(10, 1),即初次扩容长度为10。
而本文讨论的溢出情况更多是第二种情况,即非空元素集合
如果是非空元素集合,则直接返回minCapacity,那么接着作为形参传入ensureExplicitCapacity()

private void ensureExplicitCapacity(int minCapacity) {
    //标记集合被修改了几次,和iterator中的expectedModCount对应,
    //具体在ConcurrentmodificationException异常中会体现。
    //简单来说,就是arraylist是非同步,线程不安全的集合。
    //在使用迭代器迭代的过程中,我们希望是只读不写,但却并没有阻止对集合元素的修改,于是便有可能发生迭代异常。
    //在使用迭代器的过程中,如果不是用iterator内的方法remove,而是直接调用list.remove,甚至是其他对集合元素的改动,则modCount和expectedModCount会不吻合,即抛出该异常。(iterator.remove会强制同步modCount和expectedModCount)
    modCount++;

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

这里涉及到第一个溢出点。
传入的形参minCapacity溢出,即 size + num溢出
首先数组的最大长度Integer.MAX_VALAUE = 0x7fffffff; 2 31 − 1 2^{31}-1 2311约为21亿,若:size = 1numNew= MAX_VALAUE , 则此时minCapacity = MAX_VALAUE + 1,发生溢出。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); //和newCapacity = oldCapacity * 1.5 不同,*1.5后自动类型转换为double就不再溢出了
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8       一些JVM会保留头文件信息
            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;
    }

这里面处理的基本逻辑是(先不考虑溢出)
1.临时变量newCapacity是原数组长度的1.5倍
2.取newCapacity和实际需要的minCapacity中的较大值,重新赋给newCapacity
3.若newCapacityMAX_ARRAY_SIZEInteger.MAX_VALUE之间,判断实际需要的minCapacity是否也在MAX_ARRAY_SIZEInteger.MAX_VALUE之间。若在,则直接扩容到数组长度理论最大值,若不在,则扩容到MAX_ARRAY_SIZE

那么结合第一种溢出情况,我们可以总结以下两种溢出:
1.需求的最小长度溢出,即minCapacity = size + num溢出
2.原数组长度扩容1.5倍溢出,即newCapacity = oldCapacity + (oldCapacity >> 1)溢出
如果直观点写,可以写成类似于


if (minCapacity < 0){//需求最小的扩容长度直接溢出,最严重的情况,直接抛异常
	throw exception... 
}else if (newCapacity < 0){//1.5倍扩容溢出,则取需求最小扩容长度

	newCapacity = minCapacity 
	if (newCapacity > MAX_ARRAY_SIZE){ //对临界长度MAX_ARRAY_SIZE的优化
		newCapacity = Integer.MAX_VALUE;
	}


}else {//均未溢出,则取1.5倍扩容长度和最小扩容长度的较大值
	newCapacity = Math.max(newCapacity, minCapacity)
	if (newCapacity > MAX_ARRAY_SIZE){ //对临界长度MAX_ARRAY_SIZE的优化
		newCapacity = (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
	}
}
elementData = Arrays.copyOf(elementData, newCapacity)

而在jdk1.8中的写法逻辑更复杂一点,这里有个比较老的解答:链接,大意是关于使用if(a - b < 0) 而不是 if (a < b)的形式来比较大小,虽然也没解释清楚为什么会写成这样……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值