前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Java】ArrayList数组的扩容机制 jdk1.8

【Java】ArrayList数组的扩容机制 jdk1.8

作者头像
哈__
发布2024-04-08 21:23:14
620
发布2024-04-08 21:23:14
举报
文章被收录于专栏:哈哈熊哈哈熊
ArrayList和普通数组不同,ArrayList支持动态扩容,那么ArrayList到底是如何扩容的呢?你又是否知道ArrayList数组的初始长度是多少呢?

在开始介绍之前,我们要先介绍一下ArrayList类中的一些属性。

代码语言:javascript
复制
    /**
     * *默认初始容量。
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
    * 共享的空数组实例用于默认大小的空实例。我们
    * 将其与EMPTY_ELEMENTDATA区分开来,以了解何时膨胀多少
    * 添加第一个元素。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存放数组列表元素的数组缓冲区。
     * 数组列表的容量是这个数组缓冲区的长度。任何空的数组且满足elementData == 
     * DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * 将在添加第一个元素时扩展为DEFAULT_CAPACITY。
     */
    transient Object[] elementData; 

elementData就是我们的数据要存储的进入的数组,看上边的注释说,如果数组是空的并且满足elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的时候,数组就会被扩容为10;

那么接下来我们看一下ArrayList的三个构造方法。

1.有参构造方法

代码语言:javascript
复制
// 传入参数初始化数组的大小
public ArrayList(int initialCapacity) {
        //如果初始化的大小大于0
        if (initialCapacity > 0) {
        //为elementData初始化
            this.elementData = new Object[initialCapacity];
        //如果初始化的空间大小为0
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

2.无参构造方法

代码语言:javascript
复制
public ArrayList() {
        // elementData数组就等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

3.传入Collention元素列表

代码语言:javascript
复制
/**
*按照指定集合的迭代器返回的顺序,构造一个包含指定集合元素的列表。
**/
public ArrayList(Collection<? extends E> c) {
        //将元素列表转为数组
        Object[] a = c.toArray();
        //如果元素个数不为0
        if ((size = a.length) != 0) {
            //如果元素列表是一个ArrayList类型
            if (c.getClass() == ArrayList.class) {
                //把a赋给elementData
                elementData = a;
            } else {
                // 如果不是ArrayList类型,进行元素拷贝
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // 如果元素个数为0,将elementData赋值为空数组
            elementData = EMPTY_ELEMENTDATA;
        }
    }

ArrayList的扩容机制

我们向ArrayList中添加数据时,调用的是add()方法。我们跟进查看。首先执行ensureCapacityInternal(size + 1); 这个方法,翻译为确保内部容量,传入的参数是当前集合的元素个数再加上1,一定是加1,这样才能确保正确的添加。我们跟进到方法中查看。

代码语言:javascript
复制
public boolean add(E e) {
        //确保内部容量 传入当前集合中的元素个数在加上1
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }

这就是这样的一个确保内部容量的方法,传入了一个参数,名为最小的容量,之后调用 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))这个方法,但是这个方法中还有一个calculateCapacity(elementData, minCapacity)方法,我们先进入这个方法查看

代码语言:javascript
复制
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

calculateCapacity方法如下。他需要两个参数,一个是elementData,一个是最小的容量。如果我们的elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA这个数组的话,那么我们就返回这个最小的容量和我们内部默认容量中大的一个。如果不是这个默认数组的话直接返回最小容量。这里的判断是因为我们有两种不同的构造函数,一个是无参,另一个是有参,无参构造函数在添加数据的时候会自动将数组扩容为10。

代码语言:javascript
复制
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

这样调用完这个函数之后我们就知道数组的容量是多少了。

我们返回ensureExplicitCapacity这个函数接着看。

他需要一个参数就是最小的容量。modCount记录的是数组的修改次数。

接着判断最小的容量减去我们当前数组的容量,如果数组的空间不够,我们就要的调用grow函数进行扩容。否则的话我们就直接回到了最上方的add函数当中进行元素添加。

代码语言:javascript
复制
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

grow函数如下。别慌我写了注释。

代码语言:javascript
复制
private void grow(int minCapacity) {
        // overflow-conscious code
        // 这是我们之前数组的容量
        int oldCapacity = elementData.length;
        // 新数组的容量应该是旧数组容量+旧数组容量/2,也就是扩容了1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新的容量还是不够,那我们就直接把容量定为传入的最小容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果扩容扩出的新数组太大了,比数组最大长度还要大 要重新扩容
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 将我们的elementData进行扩容,然后赋值给我们的elementData数组,也就是把数组搬到了一个
        // 大的空间中
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

如果真的走到了hugeCapacity函数中,如下所示。

代码语言:javascript
复制
 private static int hugeCapacity(int minCapacity) {
        // 如果最小的容量小于0 直接报错
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // 如果最小容量比数组最大容量大,返回整形的最大值,否则的话就是等于数组最大值
        // 不再扩充1.5倍了
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

到了这一步真的就是走完了最最最上方的ensureCapacityInternal方法,然后就可以添加元素了。

以上内容就是ArrayList集合的扩容机制。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档