【Java】ArrayList源码分析

一、引入

先来我们来学习一下List集合中的第一个实习类:ArrayList

首先我们先来看一下 ArrayList 在整个集合体系中的位置。

image-20240505165536016

通过这个位置我们就知道了,在之前我们学习过的 CollectionList 中常见的方法在 ArrayList 中都可以拿过来直接使用,因此在 ArrayList集合 中我们就不需要再去学习常见的方法。

因此本节课就是来讲解一下 ArrayList 的底层原理,然后再去扒一扒它的源代码,看看Java是怎么去写 ArrayList 的。

有部分同学在某些资料中看过ArrayList的底层原理,大概知道两句话:

① ArrayList底层是数组结构的,数组默认长度为10

② 当数组添加满了之后,会自动扩容为1.5倍。

这两句话真正正确的只有第一句的前半段,剩下来的不完整。

image-20240426093055772

正确的情况我会给你一一道来。


二、底层原理分析

1)引入

我们先来看一段代码:首先创建 ArrayList集合 的对象,再调用 add() 添加一些数据,这样我们可以一次添加一个数据。

但是在 ArrayList 中,还有一个 addAll() 方法,可以一次批量添加多个数据。

例如 list2.addAll(list1) 中,它就可以将 list1 集合中的数据全都放到 list2 中。

image-20240426093955872

因此我们在看源码的时候需要将这两种情况都考虑进去:① 一次添加一个;② 一次添加多个


2)情况1:一次添加一个

① 当我们利用空参构造创建ArrayList对象的时候,Java在底层会创建一个默认长度为0的数组

并且数组有一个名字:elementData,与此同时,Java在底层还会有一个成员变量 size,它可以用来记录元素的个数。

image-20240426094501778

② 当调用 add() 添加第一个元素时,底层会创建一个新的长度为10的数组。

数组中默认初始化值为 null,因此我们也会认为 ArrayList 底层的数组默认长度是 10

PS:不是创建集合的时候这个数组就有了,而是添加第一个元素的时候这个数组才出现。

image-20240426094555448

假设现在添加的第一个元素是 "a",我们就可以将 "a" 放到 0索引 的位置,添加完成后 size++,此时 size 的值就是 1

有很多人会认为此时集合的长度就是 1,因为现在集合中只有一个元素。

确实是 1,但是 size变量 其实有两层含义:1、表示元素个数;2、下一个存入位置。

image-20240426094842386

例如我现在要添加元素 "b",那么 "b" 就应该放在 1索引,与此同时 size++ 变成 2

此时这个 size 就表示:1、集合中现在有两个元素;2、再添加下一个元素的时候就应该放在2索引。

image-20240426095134261

当我们不断的添加,数组里面的元素存满了,这个时候如果需要继续再存,在底层就需要自动扩容。


③ 存满时,会扩容1.5倍

当原来10个元素的数组存满时,它会创建一个新的数组,新数组长度是原来的1.5倍,也就是长度为15。

然后再将旧数组中所有的元素全部拷贝到新数组中。

当我们还要添加新元素时,就在 10索引 往后加就ok。

image-20240426095458427

但如果现在这个长度为15的数组也加满了,就会继续扩容为原来的 1.5倍。


3)情况二:一次添加多个

如果不是一个一个加的,而是一次添加多个数据,1.5倍还放不下,则新创建数组的长度为实际为准。

假设集合第一次新建的长度为10的数组已经装满了,然后此时我需要一次性添加100个数据,很显然,长度为10的数组扩容1.5倍为15个数据的数组,肯定不够,此时新数组的长度就以实际情况为准:110(原来的10个 + 新增加的100个数据)。

image-20240426095723191


三、查看源码

打开IDEA,ctrl + N 搜索 java.lang包 下的 ArrayList

image-20240426101007102

这个类的源码比较多,看起来不太方便,因此我们需要将它的大纲视图罗列出来,有两种办法

alt + 数字7 ,此时在左边就会有 ArrayList 的大纲。你想要看哪个方法直接用鼠标点就行了。

image-20240426101449804

但是在左边感觉看着有点不舒服,因此我们可以将它移动到右边去

image-20240426101536926

ctrl + F12 ,也可以出现这个大纲视图,并且还可以输入一些字母进行搜索。

例如输入 add(),那么它的相关方法也都出现了。

image-20240426101639416

这里我们采用的是 alt + 数字7 将整个大纲都罗列出来的方式。

首先来看一下ArrayList的空参构造。


1)空参构造

在右边点击一下空参构造,然后IDEA就会自动跳转到空参构造这里。

image-20240426102011209

空参构造里面它只有一行代码。

image-20240426102512320

选中 elemetDate ctrl + b 可以发现它是一个数组。

在刚刚我们说了,ArrayList 底层其实就是数组,数组的名字就叫做 elementDate

image-20240426102429498

如果我们使用空参创建,你认为是将什么东西赋值给了 elementDate 呢?

选中 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ctrl + b 跟进,此时就会跳转到 130行,此时就发现了,它就是一个 长度为0 的数组!

image-20240426102723631

因此就可以得出刚刚的结论:当我们用空参构造创建对象的时候,它在底层创建了一个长度为 0 的数组。

与此同时,我们还需要来看一下 size 成员变量,默认初始化值也是 0,它就表示集合中现在元素的个数。

image-20240426102818569

2)boolean add(E e)

在右边找到 add(E e),此时它就会跳转到这个方法。

这个 add方法 中又调用了另一个 add方法,选中它继续跟进。

image-20240426102939952

可以发现这个方法里面只有四行代码,但并不是就这么简单

首先它会去判断你当前元素的位置 if (s == elementData.length),如果数组满了,就需要调用 grow() 进行扩容,继续跟进 grow()

image-20240426103103445

此时它又调用了另一个有参的 grow() 方法,继续跟进

image-20240426103400443

在扩容的时候还需要计算新数组的长度 newCapacity,最后再调用 Arrays 中的 copyOf() 进行复制。

这个过程来讲稍微有点麻烦,如果我们用眼睛一个个去看,几乎是不可能看懂的。

image-20240426103450709

因此这里将里面的代码都罗列出来了,我们一个一个去讲解。


下面过程还是听视频讲解比较好:集合进阶-06-ArrayList源码分析_哔哩哔哩_bilibili,定位到 12:04

情况2:第一次添加数据

image-20240426104143924

情况2:当长度的数组已经装满了。

PS:下面 newLength() 中默认新增的容量为 oldCapacity >> 1,即 除以2,原来的容量加上默认新增的容量,合起来就是 1.5倍

image-20240426104842871

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值