【源码】走一遍源码弄清ArrayList容器的扩容机制

26 篇文章 6 订阅
8 篇文章 0 订阅

【源码】走一遍源码弄清ArrayList容器的扩容机制

首先我们来看看ArraysList容器在整个Java集合框架中所处的位置

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

由此可见ArrayList是Java集合框架中,两大派系中Collection接口的子接口List的实现类

我们从源码入手可以看到ArrayList底层数据结构实际上是一个**Object类型的数组**,由于Object是所有类的父类,所以ArrayList集合可以存放任意类型的元素,并且存放的元素是有序的、可重复的、可随机访问的。

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

接下来我们就以add()方法为突破口,来解读一下ArrayList的源码,以及它的扩容机制

我们要想向ArrayList结合添加元素,首先得创建出一个ArrayList对象出来,ArrayList的构造方法有3种:

  • 无参构造器

    使用无参构造器创建一个对象时,默认创建的是一个空集合,它的初始容量是10。什么鬼?空集合怎么还容量为10,这不自相矛盾吗?我这里说的空集合指的是,这个集合刚创建出来,在还没有调用add()方法向集合中添加第一个元素之前,这个集合是空的。而所说的初始容量为10,指的是当你调用add()方法向集合中添加第一个元素时,这个集合的容量大小会被扩容到10,这个10就是指的初始容量。可以我们为向容器中添加了一个元素,为什么要扩容到10呢?这不浪费内存空间吗?实际上这是考虑的扩容的问题,你创建这个容器肯定不止存放一个元素,我们之所以冗余了一些空间,就是为了防止不断频繁扩容导致性能低下的问题。
    在这里插入图片描述

  • 带初始容量参数的构造函数

    我们在创建一个容器时,在构造方法中可以传入初始容量大小,如果传入的初始容量大小为0的话,就会创建一个空的容器。

    注意!这里的DEFAULTCAPACITY_EMPTY_ELEMENTDATA和无参构造方法中的EMPTY_ELEMENTDATA虽然都是空集合,但是这两个空集合是有区别的,DEFAULTCAPACITY_EMPTY_ELEMENTDATA在添加第一个元素时,明确指定了扩容到默认的初始容量大小。而EMPTY_ELEMENTDATA由于指定的初始容量为0,所以在扩容的时候,会调用grow()方法走扩容的流程。
    在这里插入图片描述

  • 传入一个集合参数的构造器

    在创建ArrayList容器时,可以给构造器传入一个集合参数,利用Arrays.copy()方法复制这些集合中元素,原来创建一个新的集合
    在这里插入图片描述

现在我们利用构造器创建出来了一个ArrayList容器,接下来就调用add()方法开始向容器中添加元素。

在向容器中添加元素时,首先会调用ensureCapacityInternal()方法确保容器容量足够得到最小扩容量

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

如果是使用默认的无参构造器创建的容器,并且是添加第一个元素,minCapacity 为 10,也就是当前添加元素的操作,需要容器的容量大小为minCapacity

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

确定了最小容器容量之后,就调用ensureExplicitCapacity()方法根据得到的最小容量来判断到底需不需要进行扩容

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

只有当需要的最小容量大于当前容器的最大容量时,最大的容量也就是数组的长度,就会调用grow()方法进行扩容

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

grow()方法可以看出ArrayList容器的扩容机制是变为原来容量的1.5

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

为了防止在添加第一个元素时,由于空集合的容量为0,扩大1.5倍之后计算出的新的容量还是0,这样情况,源码中还通过比较扩容后的新的容量与需要的最小容量,来判断是否是第一次添加元素,如果是就直接将扩容后的新的容量改成需要的最小容量。这样就避免了第一次扩容扩不起来的问题。

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

如果说扩容后的容量超过了ArrayList的最大容量,也就是扩容1.5倍后的容量大于了Integer.MAX_VALUE - 8。此时就需要判断需要的最小容量是否超过了ArrayList的最大容量。
在这里插入图片描述

如果需要的最小容量超过了ArrayList最大容量,此时我们就不能按照1.5倍扩容了,直接返回Integer.Max_VALUE,如果说没有超过ArrayList最大容量,就返回最大容量

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

扩容完之后,就调用Arrays.copyOf()方法,开始将原来容器中的所有元素复制到扩容后的容量中

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

Arrays.copyOf()方法底层调用的实际上是System.arraycopy()本地方法,将原数组中的数据进行拷贝,并返回新的数组
在这里插入图片描述

至此,ArrayList容器的扩容流程就完成了!

我们注意到ArrayList的增删改查相关的方法都没有使用Synchronized同步关键字,或者说没有使用其他同步机制,因此ArrayList容器是线程不安全的!

ArrayList容器对标的是Vector这个容器

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

Vector的底层实现和ArrayList的底层实现类似,也是使用Object类型的数组存放元素。而且默认初始容量也为10
在这里插入图片描述

与ArrayList不同的地方在于Vector的扩容的方法使用了Synchronized同步关键字,因此是**线程安全的!**而且Vector扩容机制是扩大2倍!而ArrayList是扩大1.5倍!
在这里插入图片描述

一般开发中都是使用ArrayList容器,Vector容器基本上不再使用,虽然Vector使用Synchronized保证了线程安全,但是效率十分低下,如果考虑到线程安全问题,可以使用JUC并发集合!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值