集合框架——ArrayList集合扩容机制源码分析

一、使用无参构造器创建ArrayList集合

1. 示例代码

public class ArrayListSource {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();  //跳转至第一步
        for (int i = 0; i < 10; i++) {
            arrayList.add(i);  //需要进行第一次扩容,跳转至第二步
        }
        for (int i = 11; i <= 15; i++) {
            arrayList.add(i);  //需要进行第二次扩容
        }
        arrayList.add(100);  //需要进行第三次扩容
        arrayList.add(200);
        arrayList.add(300);
    }
}

2. 源码分析

第一步:
当使用new ArrayList()创建集合时,会调用ArrayList类的无参构造器,在集合内部存在一个空的elementData数组,代码如下

private static final int DEFAULT_CAPACITY = 10;  //默认容量
...
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  //默认空数组
...
transient Object[] elementData;  //存放Object对象的数组
...
private int size;  //集合中所包含的元素,默认为0
...
protected transient int modCount = 0;
...
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //MAX_ARRAY_SIZE = 2147483639
...
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;  //elementData初始化为{}数组,其中size=0 
}

第二步:
程序进入for循环,从i=0开始,执行arrayList.add(i)方法,进入ArrayList类中

public boolean add(E e) {  //此时:e=1
        ensureCapacityInternal(size + 1);  //跳转至第三步
        elementData[size++] = e;
        return true;
}

第三步:
执行ensureCapacityInternal(size + 1),其中size=0

private void ensureCapacityInternal(int minCapacity) {  //此时minCapacity=size+1=1,即给集合中添加1个元素,需要的最小容量是1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  //跳转至第四步
}

第四步:
先执行ensureExplicitCapacity()中的嵌套函数calculateCapacity(elementData, minCapacity)

// elementData = {}
// minCapacity = 1
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
// DEFAULT_CAPACITY = 10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  //此if语句成立
            return Math.max(DEFAULT_CAPACITY, minCapacity);  //返回值为10,退出函数,跳转至第五步,
        }
        return minCapacity;
}

第五步:
执行ensureExplicitCapacity()函数

// minCapacity = 10
// modCount默认为0,然后自加1
// elementData.length = 0
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)  //此时if语句成立
            grow(minCapacity);  //跳转至第六步
}

第六步:
执行grow(minCapacity)

// minCapacity = 10
// MAX_ARRAY_SIZE = 2147483639
private void grow(int minCapacity) {
        int oldCapacity = elementData.length; //oldCapacity=0
        int newCapacity = oldCapacity + (oldCapacity >> 1);  //newCapacity=0+0/2=0
        if (newCapacity - minCapacity < 0)  //此if语句成立
            newCapacity = minCapacity;  //newCapacity = 10
        if (newCapacity - MAX_ARRAY_SIZE > 0)  //此if语句不成立
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
        //此语句执行后,elementData = {null,null,null,null,null,null,null,null,null,null}
}

第七步:
当程序执行完第六步之后,根据方法调用步骤依次返回,直至第二步的第2条程序语句

public boolean add(E e) {//此时:e=1
        ensureCapacityInternal(size + 1);
        //通过以上方法,确保集合中可以存放e对象
        elementData[size++] = e;//此时size=0,之后自加1;e=1
        //执行之后 elementData = {1,null,null,null,null,null,null,null,null,null}
        return true;
}

第八步:

在for循环中,不断执行 arrayList.add(i)方法,直到for循环结束,以上步骤介绍了ArrayList第一次默认初始化之后存放元素的步骤和扩容机制,当集合中存放的对象达到容量10时,集合需要再次进行扩容。而接下来的每次扩容的容量=原来容量*1.5,即 0 --> 10 --> 15 --> 22 --> 33…


二、使用带参数的构造器创建ArrayList集合

1. 示例代码

public class ArrayListSource {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList(5);  //跳转至第一步
        for (int i = 0; i < 50; i++) {
            arrayList.add(i);
        }
    }
}

2. 底层源码

第一步:

public ArrayList(int initialCapacity) {  //initialCapacity = 5
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];  //elementData = {null, null,null,null,null}
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
}

由上ArrayList带参构造器内容可知:

  1. 当初始容量显示的设置为0时,与默认无参构造器执行方式相同,都是将elementData设置为空数组。
  2. 当初始容量大于0时,将创建一个Object类型数组,数组大小等于初始容量。
  3. 当初始容量小于0时,将抛出异常对象IllegalArgumentException

第二步:
创建完成arrayList集合之后,进入for循环,从i=0开始,执行arrayList.add(i)方法,底层代码如下:

public boolean add(E e) {  //e = 0
        modCount++;  //modCount=0,执行之后自加1
        add(e, elementData, size);  //跳转至第三步
        return true;
}

第三步:

//e = 0
//elementData = {null, null,null,null,null}
//elementData.length = 5
//s = 0
private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)  //此时if语句不成立
            elementData = grow();
        elementData[s] = e;  //elementData = {{0, null,null,null,null}
        size = s + 1;  //size = 1
}

第四步:
程序继续执行for循环,直到size=5时,表示集合中元素已经达到容量上限,程序执行第三步中的if语句,对集合进行扩容

private Object[] grow() {
        return grow(size + 1);  //size = 6,跳转至第五步
}

第五步:

//elementData.length = 5
//minCapacity = 6
private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;  //oldCapacity = 5
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {  //if语句条件成立
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);  //跳转至第六步
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
}

第六步:

//oldLength = 5
//minGrowth = 1
//prefGrowth = 2
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // assert oldLength >= 0
        // assert minGrowth > 0
        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;  //newLength = 7
        if (newLength - MAX_ARRAY_LENGTH <= 0) {  //if条件成立
            return newLength;  //返回第5步
        }
        return hugeLength(oldLength, minGrowth);
}

第七步:
程序执行elementData = Arrays.copyOf(elementData, newCapacity);
elementData = {0 , 1 , 2 , 3 , 4 , null , null , null}
程序继续执行for循环,当达到集合的容量上限值,继续进行扩容,扩容后的容量=原容量*1.5

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ArrayList扩容机制源码如下: ```java private void ensureCapacityInternal(int minCapacity) { // 如果当前容量不足,则需要进行扩容操作 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果当前容量是默认值,则需要将其扩容为默认容量或者minCapacity minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果需要进行扩容,则进行扩容操作 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // 当前容量 int oldCapacity = elementData.length; // 扩容后的容量 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果扩容后的容量仍然小于需要的最小容量,则直接使用需要的最小容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果扩容后的容量超过了ArrayList最大容量,则进行特殊处理 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 创建新的数组,并将原数组中的元素复制到新数组中 elementData = Arrays.copyOf(elementData, newCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } ``` 在上述代码中,`ensureCapacityInternal`方法首先判断当前容量是否足够,如果不足,则调用`ensureExplicitCapacity`方法进行扩容操作。`ensureExplicitCapacity`方法会比较需要的最小容量和当前容量的差值,如果超过了当前容量,则进行扩容操作。`grow`方法是扩容的核心方法,它会首先计算扩容后的容量,然后根据扩容后的容量创建新的数组,并将原数组中的元素复制到新数组中。如果扩容后的容量超过了ArrayList最大容量,则进行特殊处理。`hugeCapacity`方法用于计算需要的最大容量,如果超过了最大容量,则抛出OutOfMemoryError异常。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值