ArrayList的初始化容量及扩容

本文详细解析了Java ArrayList的构造方法,特别是有参和无参构造的区别,以及add(E e)方法如何确保容量。在添加元素时,ArrayList会进行扩容,其扩容策略是旧容量加上旧容量的一半。通过测试,展示了不同初始容量下达到特定大小所需扩容的次数,强调了合理设置初始化容量对于避免过多扩容、提升性能的重要性。
摘要由CSDN通过智能技术生成

jdk1.8为例

1.ArrayList类构造方法

a.有参构造

//initcalCapacity指定的初始化容量 
 public ArrayList(int initialCapacity) {
 
        if (initialCapacity > 0) {
        //初始化指定的
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        //==0的话 默认容量是10    
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
        //<0的话会报异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
b.无参构造
/**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

无参构造指定底层elementData数组指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
有参构造指定底层elecmentData数组指向EMPTY_ELEMENTDATA数组
他们两个都是空数组

/**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * //用于默认大小的空实例的共享空数组实例。
     * 我们将其与 EMPTY_ELEMENTDATA 区分开来,
     * 以了解在添加第一个元素时要膨胀多少。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

都是空数组,只是DEFAULTCAPACITY_EMPTY_ELEMENTDATA用来连接在添加第一个元素时要膨胀多少。

2.add(E e)方法

   public boolean add(E e) {
   		//确保内部容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //在底层数组默认添加元素
        elementData[size++] = e;
        //返回true添加成功
        return true;
    }

测试:添加一个元素执行哪些操作

 @Test
    public void test(){
        ArrayList list = new ArrayList<>();
        list.add(1);
    }

调用栈:

在这里插入图片描述
具体的方法

计算容量的方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
		//如果是通过无参构造方法构造的
		//返回默认容量10和最小容量(size+1)的最大值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果是有参构造 直接返回size+1
        return minCapacity;
    }
//确保容量的方法
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	//确保显示容量的方法
    private void ensureExplicitCapacity(int minCapacity) {
   	 //modCount由该类迭代器所使用,如果此字段发生了更改,迭代器在使用next(),remove()方法时抛出
   	 //ConcurrentModificationException异常
        modCount++;

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

容量不足时,需要扩容。
grow为扩容方法。

3.扩容代码

  private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新容量是 等于 旧容量+旧容量的0.5倍
        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);
    }

新容量是 等于 旧容量+旧容量的0.5倍
如果新容量超过了最大值则扩容值为整型数组最大值
elementData = Arrays.copyOf(elementData, newCapacity)通过底层的复制方法将原有数据复制过来

测试扩容次数代码:

@Test
    public void test(){
        int oldCapacity = 10;
        int newCapacity = 0;
        int growCount=0;

        while(newCapacity < 1000){


            newCapacity = oldCapacity + oldCapacity / 2;
            oldCapacity = newCapacity;

            growCount++;
        }
        System.out.println("容量达到1000需要扩容"+growCount);

        newCapacity = 0;
        oldCapacity=10;
        growCount = 0;
        while(newCapacity < 10000){

            newCapacity = oldCapacity + oldCapacity / 2;
            oldCapacity = newCapacity;

            growCount++;
        }
        System.out.println("容量达到10000需要扩容"+growCount);

        newCapacity = 0;
        oldCapacity=10;
        growCount = 0;
        while(newCapacity < 100000){


            newCapacity = oldCapacity + oldCapacity / 2;
            oldCapacity = newCapacity;

            growCount++;
        }
        System.out.println("容量达到100000需要扩容"+growCount);

        newCapacity = 0;
        oldCapacity=10;
        growCount = 0;
        while(newCapacity < 1000000){


            newCapacity = oldCapacity + oldCapacity / 2;
            oldCapacity = newCapacity;

            growCount++;
        }
        System.out.println("容量达到1000000需要扩容"+growCount);

    }

结果为:
容量达到1000需要扩容12
容量达到10000需要扩容18
容量达到100000需要扩容23
容量达到1000000需要扩容29

根据实际情况,分配一个初始化的容量很有必要的
容量太大,数据增长缓慢,浪费内存。
容量太小,扩容次数变多,影响性能。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值