ArrayList:初始化和扩容机制的源码分析

    private static final Object[] EMPTY_ELEMENTDATA = new Object[0];	//1
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];	//2
	transient Object[] elementData;	//注意:这里elementData的大小指的是容量的大小,而不是实际存储的大小
    public ArrayList(int var1) {	//7
        if (var1 > 0) {	
            this.elementData = new Object[var1];	//3
        } else {
            if (var1 != 0) {	//4
                throw new IllegalArgumentException("Illegal Capacity: " + var1);
            }

            this.elementData = EMPTY_ELEMENTDATA;	//5
        }
    }
    
    public ArrayList() {	//8
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;		//6
    }

从ArrayList的带整型参数初始化方法(注释7)中,可以看到,当传入参数是大于0的数字,将会创建var1大小的数组(3)。
由此也可以看出:ArrayList底层是基于数组实现的。
当var1的值不等于0(4),则说明输入的是不合法的值,因此应该报IllegalArgumentException非法参数异常。
如果输入的容量值var1是0,那么值为EMPTY_ELEMENTDATA(2),即创建一个空数组。
如果创建ArrayList的时候采用的是无参构造方法(8),那么在初始化的时候,容量大小为DEFAULTCAPACITY_EMPTY_ELEMENTDATA(2),创建的也是一个长度为0的数组。
由此我们可以得出:ArrayList初始化的容量大小是0.

接下来查看add方法:

    public boolean add(E var1) {
        this.ensureCapacityInternal(this.size + 1);	//1
        this.elementData[this.size++] = var1;
        return true;
    }
    private void ensureCapacityInternal(int var1) {
	    if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {	//2
	        var1 = Math.max(10, var1);
	    }
    	this.ensureExplicitCapacity(var1);	//3
	}

在add的方法我们可以看到第一行语句(1),调用了ensureCapacityInternal方法,在该方法中我们可以看到第一行语句(2)就是判断当前容量(elementData )是否是默认值,如果是默认值,那么比较var1(传入的参数,为当前size+1的值)与10的大小。如果当前数组长度小于10,var1的值将为10。使用var1的值究竟要干嘛呢?我们继续追踪下去(3):

    private void ensureExplicitCapacity(int var1) {
        ++this.modCount;
        if (var1 - this.elementData.length > 0) {			//3
            this.grow(var1);	//1
        }
    }
    private void grow(int var1) {
        int var2 = this.elementData.length;
        int var3 = var2 + (var2 >> 1);		//3
        if (var3 - var1 < 0) {		//4
            var3 = var1;
        }
        if (var3 - 2147483639 > 0) {	//主要是对当前容量大小进行限制,避免超过限制大小。
            var3 = hugeCapacity(var1);
        }
        this.elementData = Arrays.copyOf(this.elementData, var3);	//2
    }

通过追踪ensureExplicitCapacity方法,我们可以看到在方法里(3)先判断了当前需要的容量大小(var1)是否大于原容量大小(elementData.length),只有当前容量大小无法满足所需容量,才会进入grow方法进行扩容。它把var1的值传入了grow方法(1)。
进入grow方法,首先var2的值为当前容量的大小,在注释3的位置,var3=当前容量的大小(var2)+当前容量的一半(>>:右移运算符,在保证没有溢出的情况下,右移n位,相当于除以2的n次方)。
由此我们可以得出:ArrayList每次进行扩容时,都是扩大到原来的1.5倍。
var3则为我们扩容后的容量大小,但当第一次添加元素的时候,elementData.length值为0,因此会出现var3为0的情况。所以我们使用注释4处的判断,来避免这一情况,使第一次添加元素时,ArrayList扩容的值保持原值10。
接着,我们可以看到注释2处的语句,使用了Arrays.copyOf方法,那么这个方法有什么用呢?我们进行测试一下:
在这里插入图片描述
可以看到使用这个方法,会利用原本的数组,拷贝一个自定义长度(传入的第二个参数值)的数组。如果定义的长度小于原数组长度,那么就截取原数组;如果大于,则就用0扩充。因此我们可以知道,ArraryList在扩容时会重新创建一个数组,将原来的数据复制过去,然后数组指向新的地址,这是十分损耗性能的。
至此我们知道了,var1被传入到了grow方法中,完成了数组的扩充。在前面已经知道当ArrayList的容量小于10时,var1传入的值为10。
由此我们可以得出:ArrayList初始化的容量大小为0,只有在第一次调用add方法的时候,默认创建容量10的空间。如果还需要扩容,则扩充到原来的1.5倍。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值