ArrayList自动扩容机制(JDK1.8)

ArrayList类的成员变量

//默认的数组容量
private static final int DEFAULT_CAPACITY = 10;
//一个空的Object数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//一个空的Object数组,同上面的区别是该变量只用于无参构造,表示ArrayList对象刚执行完空参构造,需要与上边的变量区分
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//实际存储数据的数组,ArrayList的底层实现就是Object数组
transient Object[] elementData; 
//elementData已使用的大小
private int size;

ArrayList的构造方法 

//调用空参构造时,elementData初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//传递int类型参数时,若initialCapacity>0 则 elementData初始化为指定大小的Object数组(大小为initialCapacity)。
//若initialCapacity==0,则elementData初始化为EMPTY_ELEMENTDATA,表示初始化时指定了大小为0。
//若initialCapacity<0,则抛出非法参数异常。
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

需要注意的地方是,在初始化过程中,size变量没有赋值,采用的是int类型初始化时的默认值。 

add方法

public boolean add(E e) {
    //将扩容之后的数组大小作为参数传入
    ensureCapacityInternal(size + 1);
    //将e放入数组末尾之后,自增
    elementData[size++] = e;
    //返回true,表示插入成功
    return true;
}

 方法内容很简单

  1. 确保容量够用,其中ensureCapacityInternal方法是扩容机制的重中之重,核心部分就在于此。
  2. 将传入的参数赋值到elementData的末尾,size自增
  3. 返回true,表示元素增加成功

ensureCapacityInternal(int minCapacity)方法

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

该方法中首先调用 calculateCapacity计算最小容量

//若elementData是调用空参构造进行初始化,则返回DEFAULT_CAPACITY 和minCapacity中的较大值。DEFAULT_CAPACITY为10。
//否则表示elementData在初始化时指定了数组大小,或者其中已经有值。直接返回minCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

 

计算得到当前数组的最小容量之后,将其作为参数调用ensureExplicitCapacity(minCapacity)方法

private void ensureExplicitCapacity(int minCapacity) {
    modCount++; //该变量记录的是list对象的修改次数,add,remove方法都会是该参数自增。

    //当插入数据之后的数组容量比elementData声明时的大小要大时,需要对数组进行扩容,调用grow方法进行数组的赋值
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

 

该方法中的逻辑也相当简单

首先modCount自增,这个变量用于记录list的写操作次数,调用add或者remove方法时都会自增。这个字段的具体作用可以参考

https://www.cnblogs.com/zuochengsi-9/p/7050351.html  这篇文章中的解析比较详细,此处不再赘述。

然后比较插入数据之后的数组大小,和elementData声明时的数组长度(不是已使用的长度,需要区分开),若minCapacity大于数组声明长度时,表示数组目前数组已经装满,想要再往里塞一个数据,就需要对数组进行扩容,调用grow(minCapacity)方法。

这个方法是自动扩容的核心实现,需要深刻理解。那我们再来看下grow(minCapacity)方法中都干了些什么

private void grow(int minCapacity) {
    // 根据elementData的声明长度计算新数组的长度,计算方法为 原数组长度+原数组长度的一半。使用移位操作可以加快计算速度
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果若扩容后的长度还是比minCapacity效的话,则将minCapacity作为新数组的长度,这个情况只发生在数组长度为0时,计算出的newCapacity为0,并且minCapacity取用的是数组默认大小10
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //若新数组长度比MAX_ARRAY_SIZE要大时,重新计算新数组长度,根据minCapacity计算,minCapacity大于MAX_ARRAY_SIZE时返回Integer.MAX_VALUE,否则返回Integer.MAX_VALUE-8作为新数组长度
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //对elementData进行复制,复制的长度为新数组长度。
    elementData = Arrays.copyOf(elementData, newCapacity);
}
 

所以,在我们创建ArrayList对象时,可以有意识地调用有参构造,减少第一次操作时的自动扩容步骤

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值