ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java ArrayList扩容机制

2021-07-17 22:03:08  阅读:215  来源: 互联网

标签:扩容 Object Java 容量 int ArrayList elementData oldCapacity


java ArrayList扩容机制jdk16


本文基于jdk16,其他版本jdk思想是相同的,只不过调用的方法可能有所不同,本文如果存在问题,请求大佬给予指点。

1、无参构造

ArrayList使用无参构构造,第一次添加将ArrayList中存放数据的elementData容量扩容为10

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KULPOqyR-1626529609655)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210717204128917.png)]

从上面断点处进入ArrayList的无参构造中

//ArrayList的元素都被存储在elementData中   此处使用transient关键字代表该字段为瞬时态,无法序列化
transient Object[] elementData;
//无参构造初始化赋值给elementData的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//ArrayList的无参构造
public ArrayList() {
    //将elementData赋值为空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

第一次给arrayList添加元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcOKeVQu-1626529609660)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210717204718378.png)]

从上面断点中进入以下源码

//此处的modCount意义为arrayList被修改的次数   防止多线程操作ArrayList导致出现错误
protected transient int modCount = 0;
//此处的size代表的是当前需要赋值的索引值,int类型默认为0
private int size;

public boolean add(E e) {
    //被修改的次数加1
    modCount++;
    //调用add方法
    add(e, elementData, size);
    return true;
}

从上面add(e, elementData, size);进入到下面的方法中

private void add(E e, Object[] elementData, int s) {
    //此处判断当前需要赋值的索引值是否等于elementData的长度
    //如果相等的话,说明当前的elementData的容量不足了,需要进行扩容
    if (s == elementData.length)
        //调用grow()函数对elementData进行扩容
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

上面代码中调用的grow()函数

private Object[] grow() {
    //上面代码中调用的grow()函数
    return grow(size + 1);
}

上面代码中进入的函数,第一次添加元素,走else

private static final int DEFAULT_CAPACITY = 10;

//上面代码中进入的函数
//minCapacity此处为1
private Object[] grow(int minCapacity) {
    //目前的容量即elementData的当前长度
    int oldCapacity = elementData.length;
    //如果当前的elementData不为无参构造所赋值的默认空数组进入if中,否则进入else
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //newCapacity为新的容量 大概意思为在oldCapacity的基础上在加上oldCapacity >> 1的值
        //即相当于int newCapacity = oldCapacity + (oldCapacity >> 1)
        //也相当于int newCapacity = oldCapacity + oldCapacity * 0.5  
        //newCapacity为oldCapacity乘以1.5
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        //从DEFAULT_CAPACITY和minCapacity中取出最大数,创建数组
        //因为只有elementData为无参构造所赋值的默认空数组才会进入这里
        //说明当使用无参构造创建ArrayList时,当第一次添加元素时,会将容量扩容为10
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

从上面代码返回回去,到下面代码中,将元素添加到elementData中

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

第一次添加元素,调用无参构造,将容量扩容为了10,若之后还需再次扩容,则会将容量扩展为原来的1.5倍

在这里插入图片描述

当不需要扩容时,会在下面的语句中直接进行元素的添加,并返回

private void add(E e, Object[] elementData, int s) {
    //此处判断当前需要赋值的索引值是否等于elementData的长度
    //此时if语句不满足,直接进行赋值
    if (s == elementData.length)
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

中间一些步骤与上面相同,不同的只有扩容时不同,此处只展示扩容时,中间步骤跳过

//此时minCapacity为size加1,之前添加了10个元素,所以这里为11
private Object[] grow(int minCapacity) {
    //oldCapacity为当前elementData的长度即为10
    int oldCapacity = elementData.length;
    //if判断满足,进入if中
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //newCapacity为新的容量 大概意思为在oldCapacity的基础上在加上oldCapacity >> 1的值
        //即相当于int newCapacity = oldCapacity + (oldCapacity >> 1)
        //也相当于int newCapacity = oldCapacity + oldCapacity * 0.5  
        //newCapacity为oldCapacity乘以1.5
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        //将得到的新的elementData返回  通过Arrays.copyOf得到的数组会保留之前的数据
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

2、有参构造

如果使用指定容量的构造器,则初始elementData的容量为指定容量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xN09IHxd-1626529609665)(C:\Users\14538\AppData\Roaming\Typora\typora-user-images\image-20210717212905221.png)]

从当前断点进入到有参构造中

private static final Object[] EMPTY_ELEMENTDATA = {};

//ArrayList有参构造   initialCapacity创建对象时传入的容量大小
public ArrayList(int initialCapacity) {
    //如果容量大小大于0,则创建指定容量的数组进行返回
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    //如果容量大小等0,则创建的数组为空数组,与无参构造相同
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

总结:

1、ArrayList中维护了一个Object类型的数组elementData

transient Object[] elementData;

2、当使用无参构造创建ArrayList对象时,则初始化的elementData容量为0,当添加第一个元素时,会扩容为10,如需再次扩容,则扩容为原来容量的1.5倍(代码中是在原来容量的基础上加上原来容量数右移1位)

3、当使用有参构造创建ArrayList对象时,则初始化的elementData容量为指定容量大小,如果需要再次扩容,则扩容为原来容量的1.5倍(代码中是在原来容量的基础上加上原来容量数右移1位)

标签:扩容,Object,Java,容量,int,ArrayList,elementData,oldCapacity
来源: https://blog.csdn.net/weixin_43546857/article/details/118863165

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有