JAVA_ArrayList添加元素时的源码分析(jdk17)

目录

ArrayList 在 Collection 中的位置:

ArrayList 集合底层原理:

先总结:

ArrayList 底层是数组结构的:查找快,增删慢

看源码:

看一些重要的源码:

第一次存元素:

逻辑总览图:

长度为 10 的底层数组存满后继续存:

逻辑总览图:


ArrayList 在 Collection 中的位置:

JAVA_ArrayList添加元素时的源码分析(jdk17)_第1张图片

因为 ArrayList 是 List 接口和 Collection 接口的实现类,所以它们的方法 ArrayList 也可以用。


ArrayList 集合底层原理:

先总结:

ArrayList 底层是数组结构的:查找快,增删慢

  1. 利用空参创建的集合,在底层会创建一个默认长度为 0 的数组(数组名: elementData)。
  2. 添加第一个元素时,底层会创建一个新的长度为 10 的数组
  3. 存满时,若再添加元素,数组会扩容 1.5 倍。
  4. 如果一次添加多个元素,1.5 倍还放不下,则新创建数组的长度以实际为准,(也就是说 当添加元素个数超过了原来长度的一半时,就会以实际添加量为准进行扩容
    1. 如图:

JAVA_ArrayList添加元素时的源码分析(jdk17)_第2张图片


先看一些重要的源码:

1.首先找到 ArrayList-->Alt+7 看大纲 --> 找到空参构造

JAVA_ArrayList添加元素时的源码分析(jdk17)_第3张图片

2.看到一个叫 "默认长度 _ 空 _元素数据" 赋值给了 “元素数据”

JAVA_ArrayList添加元素时的源码分析(jdk17)_第4张图片

3.发现了 这个 "默认长度 _ 空 _元素数据" 的东西 其实是一个长度为 0 的数组

4.所以当空参创建集合对象时,此时集合元素的数量是 0

JAVA_ArrayList添加元素时的源码分析(jdk17)_第5张图片


开始分析:

第一次存元素:

假设我们使用空参创建了一个集合,

并要添加一个元素“aaa”

1. 首先 add 方法接收参数,

并且 modCount++,表示集合操作次数加 1,

接着调用另一个 add 方法:

    • 第一个参数是“aaa”
    • 第二个参数是那个在创建集合时就已经创建好的的底层数组 elementDate
    • 第三个参数是集合的长度,为 0

JAVA_ArrayList添加元素时的源码分析(jdk17)_第6张图片

2.add 中的 add 方法:

因为 size 此时为 0,数组长度也为 0 所以进入 if 语句调用 grow()方法进行扩容

JAVA_ArrayList添加元素时的源码分析(jdk17)_第7张图片

3.接着来看 grow 源码:

在 grow 内也调用了一个 grow 方法,并传入参数 1,

表示添加完当前元素之后的最小容量

JAVA_ArrayList添加元素时的源码分析(jdk17)_第8张图片

4.grow 中的 grow 源码:

1 赋值给了 minCapacity

数组的长度到目前为止,没发生过变化,为 0

不满足 if 条件直接跳过,第一次添加数据的时候,会执行到else这里,

若在以后继续添加元素,就会执行 if 里面的代码

JAVA_ArrayList添加元素时的源码分析(jdk17)_第9张图片

else 内将minCapacity 和DEFAULT_CAPACITY 进行了比较,看看DEFAULT_CAPACITY 内是什么

最后第二个 grow 方法原路返回一个长度为10的新数组

5. 最后:

元素"aaa"放入底层新数组的 0 索引处,集合长度 size 变为 0+1=1。

逻辑总览图:


长度为 10 的底层数组存满后继续存:

现在假设之前底层创建的默认长度为 10 的数组已被存满了,

这时再存一个元素"aaa",看看底层逻辑是怎样的:

第一步还是看 add 方法:

首先 add 方法接收参数"aaa",

并且 modCount++,集合操作次数加 1,

接着调用另一个 add 方法:

    • 第一个参数是“aaa”
    • 第二个参数是 那个 存满 10 个元素的底层数组 elementDate
    • 第三个参数是 集合的长度,为 10

JAVA_ArrayList添加元素时的源码分析(jdk17)_第10张图片

2.add 中的 add 方法:

因为 size 此时为 10,数组长度也为 10, 所以进入 if 语句调用

grow()方法进行扩容:

JAVA_ArrayList添加元素时的源码分析(jdk17)_第11张图片

3.现在看 grow 方法:

发现在 grow 内也调用了一个 grow 方法,并传入参数 11,

表示添加完当前元素之后的最小容量

JAVA_ArrayList添加元素时的源码分析(jdk17)_第12张图片

4.grow 中的 grow 源码:

11 赋值给了 minCapacity

数组的长度到目前为止,没发生过变化,为 10,赋值给了old capacity 记录为老容量。

此时满足 if 条件(若是第一次存元素不会走 if),

在 if 内调用了一个Arrays Support 的 new Length 方法,并且传递了三个参数:

1.老容量

2. 理论增长容量 1,

3. 默认实际增长量是oldcapacity 进行右位移 1 的结果(10/21=5)。

JAVA_ArrayList添加元素时的源码分析(jdk17)_第13张图片

我们来看看 newLength 方法:

JAVA_ArrayList添加元素时的源码分析(jdk17)_第14张图片

(有个细节:这里为什么要将 minGrowth 和 preGrowth 进行比较

因为考虑到了一次添加多个元素的情况,如添加了一个长度为 100 的集合,这里的 minGrowth 就会是 100,经过比较最后取得增长量才会是 100,也就是说当添加元素个数超过了原来长度的一半时,就会以实际添加量为准进行扩容)

扩容后的长度用 preLength 接收,后面的 if 先不用看。

然后将扩容后的长度preLength =15 返回第二个 grow 方法的newCapacity 变量

JAVA_ArrayList添加元素时的源码分析(jdk17)_第15张图片

然后调用了 Arrays 的 copyOf 方法:

根据长度 newCapacity :15创建新的数组

并把老数组中的所有数据全部拷贝到这个长度为 15 的新数组当中

最后将该数组原路返回

5.最后:

元素"aaa"放入底层新数组的 10 索引处,size 变为 10+1=11。

JAVA_ArrayList添加元素时的源码分析(jdk17)_第16张图片

逻辑总览图:

你可能感兴趣的:(java,windows,数据结构)