ArrayList扩容处理

本文深入解析ArrayList在Java中的实现机制,特别是其扩容策略。当添加元素超过当前容量时,ArrayList会自动扩容,新容量通常为原容量的1.5倍。文章通过代码示例详细解释了扩容过程,包括如何计算新容量、如何复制旧数组到新数组等关键步骤。
摘要由CSDN通过智能技术生成

ArrayList是基于动态数组实现的一个数据结构,如果添加元素时,元素个数超过list的容量大小时,会涉及到扩容。

 

ArrayList的扩容是如何做的,跟着代码走最容易懂。

    /**
     * 添加元素在list尾部
     *
     * @param e 待添加元素
     * @return 返回添加成功标识
     */
    public boolean add(E e) {
        // 确保内部容量增加
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }


    /**
     * 确保list内部容量可以容纳增加的一个元素
     * 上面方法中给定list的size + 1 作为最小容量大小,因为size <= list容量capacity
     * size + 1 作为容量是绝对可以满足要求的
     */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /**
     * 计算动态数组容量
     * 对于给定的动态数组和最小容量值,计算list需要的容量大小
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 如果动态数组为空
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 则取默认容量10 和 最小容量之间大的一个,作为需要的容量返回
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 动态数组非空时,直接反馈传进来的最小容量,因为最小容量已经等于size + 1了
        return minCapacity;
    }

    /**
     * 计算明确容量
     * 对于给定的动态数组和最小容量值,计算list需要的容量大小
     */
    private void ensureExplicitCapacity(int minCapacity) {
        // list结构被修改次数加1
        modCount++;

        // 如果容量最小值大于动态数组长度,则需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * 扩充list容量,确保list至少可以容纳minCapacity个元素
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // 定义原容量长度
        int oldCapacity = elementData.length;
        // 新容量扩大到原容量的1.5倍,右移一位相关于原数值除以2,加上原容量长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 第一次存放元素时,minCapacity为10,而通过上面计算出来的newCapacity为0
        if (newCapacity - minCapacity < 0)
            // 这时 取新的容量为minCapacity
            newCapacity = minCapacity;
        // 如果新容量超过最大数组长度
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            // 进行超大容量处理,并将结果作为最大容量
            newCapacity = hugeCapacity(minCapacity);
        // 将原elementData数组元素拷贝到新的容量为newCapacity的数组中
        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;
    }

通过上面的代码,可以总结如下:

  • ArrayList无参构造方法默认容量为10
  • 如果添加的元素超出当前list容量,list容量扩充一半
  • ArrayList最大容量不超过Integer的最大值

 

代码测试

    /**
     * ArrayList容量测试
     * @throws Exception
     * @throws SecurityException
     */
    public static void arrayListCapacity() throws Exception, SecurityException {
    	// 默认容量测试
    	List<Object> list = new ArrayList<Object>();
    	Field size = list.getClass().getDeclaredField("size");
    	Field elementDataField = list.getClass().getDeclaredField("elementData");
    	size.setAccessible(true);
    	elementDataField.setAccessible(true);
    	
    	Object[] elementData;
    	
    	for (int i=0; i<30; i++) {
    		Object o = new Object();
    		list.add(o);
    		if (i==9 || i==10 || i==11 || i==14 || i==15 || i==22 || i==23 ) {
        		elementData = (Object[])elementDataField.get(list);
        		System.out.println("i=" + i + ",size=" + size.get(list) + ",动态数组地址" + elementData + ",elementData=" + elementData.length);
    		}
    	}
    	
    	System.out.println("**********分割线***********");
    	
    	// 指定容量测试
    	List<Object> list2 = new ArrayList<Object>(6);
    	Field size2 = list2.getClass().getDeclaredField("size");
    	Field elementDataField2 = list2.getClass().getDeclaredField("elementData");
    	size2.setAccessible(true);
    	elementDataField2.setAccessible(true);
    	
    	Object[] elementData2;
    	
    	for (int i=0; i<30; i++) {
    		Object o = new Object();
    		list2.add(o);
    		if (i==5 || i==6 || i==9 || i==13 || i==19 || i==28 || i==29 ) {
        		elementData2 = (Object[])elementDataField2.get(list2);
        		System.out.println("i=" + i + ",size=" + size2.get(list) + ",动态数组地址" + elementData2 + ",elementData=" + elementData2.length);
    		}
    	}
    }

运行结果

i=9,size=10,动态数组地址[Ljava.lang.Object;@6d06d69c,elementData=10
i=10,size=11,动态数组地址[Ljava.lang.Object;@7852e922,elementData=15
i=11,size=12,动态数组地址[Ljava.lang.Object;@7852e922,elementData=15
i=14,size=15,动态数组地址[Ljava.lang.Object;@7852e922,elementData=15
i=15,size=16,动态数组地址[Ljava.lang.Object;@4e25154f,elementData=22
i=22,size=23,动态数组地址[Ljava.lang.Object;@70dea4e,elementData=33
i=23,size=24,动态数组地址[Ljava.lang.Object;@70dea4e,elementData=33
**********分割线***********
i=5,size=30,动态数组地址[Ljava.lang.Object;@5c647e05,elementData=6
i=6,size=30,动态数组地址[Ljava.lang.Object;@33909752,elementData=9
i=9,size=30,动态数组地址[Ljava.lang.Object;@55f96302,elementData=13
i=13,size=30,动态数组地址[Ljava.lang.Object;@3d4eac69,elementData=19
i=19,size=30,动态数组地址[Ljava.lang.Object;@42a57993,elementData=28
i=28,size=30,动态数组地址[Ljava.lang.Object;@75b84c92,elementData=42
i=29,size=30,动态数组地址[Ljava.lang.Object;@75b84c92,elementData=42

通过测试,可以得出如下结论
1、ArrayList在存放元素超出现有数组大小时会自动扩容
2、每次扩容大小为当前容量的一半取(如无法整除则取整数位)
3、扩容后,会将当前数组中的所有元素拷贝到新的扩容数组中
4、默认容量和指定容量的ArrayList扩容规则相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值