ArrayList扩容原理,源码追踪(jdk8)

ArrayList 扩容原理

add方法(扩容机制jdk8)

写一个代码案例断点调试

package list;

import java.util.ArrayList;

/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/26 19:08
 * @Description 功能描述:案例断点查看ArrayList的源码(添加)
 */
public class ArrrayListOriginSee {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();//这里打一个断点
        for (int i = 1;i<=10;i++)
        {
            list.add(i);
        }
        for (int i =11;i<=15;i++)

        {
            list.add(i);
        }
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

image-20230726203309097

  • 1 定义列表的时候没有指定初始化容量,那么就会默认调用它的无参构造器,这里初始化了数组,是一个空的数组

image-20230726203612036

可以看到这里是一个Object类型的数组,但是是一个空的。

image-20230726203818260

这里断点返回会回到这个初始化的方法这里,可以看到初始化列表为一个空的列表

image-20230726204635427

下面继续执行

  • 2 执行add方法来观察ArrayList的扩容机制

首先执行10次添加元素

image-20230726204818896

进入方法,来到这里,其实在真正添加元素之前会执行装箱的操作,也就是Integer 的ValueOf方法

image-20230726204928611

从这里我们可以看出 :

IntegerCache.low = -128
IntegerCache.high = 127
缓冲区cache是一个Integer类型的数组
也就是说:IntegerCache缓存区间为[-128,127]。所以,在调用Integer.valueOf(int i)方法进行自动装箱时假若i的值在[-128,127]区间则生成的Integer对象会被存入缓冲区。当再次对该值进行装箱时会先去缓冲区中获取;如果取到则返回,如果没有取到则创建包装类对象存入缓冲区并返回。

一个知识点

这里是Integer 的 ValueOf方法,可以将数据转化为包装类型

Integer number00 = Integer.valueOf(123);
     Integer number02 = Integer.valueOf(123);
     System.out.println(number00==number02);//true
//        Integer.valueOf将基本类型转变为包装类型的过程中会有一个缓存的机制,就是范围在[-127~128]会存放在缓存中,那么引用就是一样的
     Integer integer = Integer.valueOf(129);
     Integer integer1 = Integer.valueOf(129);//false 超过了128那么就会重新new 一个Integer的类型,也就是创建了一个新的对象
     System.out.println(integer==integer1); //false
     Integer integer2 = Integer.valueOf(-1);
     Integer integer3 = Integer.valueOf(-1);
     System.out.println(integer2==integer3);//true

  • 继续断点

    会回到原来的断点的位置一次,然后我们再次进去的时候就来到这里,可以看到这个方法是一直返回true的

    image-20230726210106545

    然后进入到这个方法,这个方法字面意思理解就是确认容量的操作,首先第一句判断肯定是符合的,因为我们调用无参的构造方法的时候就是这句话初始化的。

    image-20230726210227040

    image-20230726210803636

    所以这里这个最小的容量会被初始化为10

    下面还有一段代码进行传入将10这个值传入

    image-20230726210857198

    进去方法

    image-20230726210950334

    然后执行下面这个方法,肯定是要执行grow的,这个其实可以理解为第一次扩容

    image-20230726211058785

    然后进入grow方法

    image-20230726211204905

    运行

    image-20230726211932427

    image-20230726212058372

    ​ 运行到这里

    image-20230726211719326

    这里进行了一个数组拷贝的操作其实就是扩容了在首次添加元素的时候

    image-20230726212121104

    然后回到这里,这里进行了元素的赋值

    image-20230726212241614

    在idea中要想看到这个完整的扩容还需要这里操作下,就是断点调试后在这个variables这里进行右键选择这里

    image-20230726213026641

    image-20230726212925548

    没有出现扩容效果的话可以这样设置然后再断点

    然后再看

    image-20230726213252752

    这里效果就出来了,所以这里会到10容量首次

  • 3然后继续添加,我们找到边界10。

    image-20230726213517765

    然后继续添加,这里就是走到下一个for循环,走到这里

    image-20230726213723369

    min变11

    image-20230726213758224

    传入

    image-20230726213819703

    传入

    image-20230726213852578

    这里首先new变15,第一个if肯定是没有执行的,这样这个newCapcticy就是15了,最后到了那个数组拷贝

    image-20230726213936519

    说明一下这个huge,其实一般是不会走到这里的,我使者循环1了添加这么多元素,实在太卡了,所以如果添加这么多元素性能会大大降低。

    image-20230727091036473

    变15容量

    image-20230726214255243

    然后继续添加的话,可以看到这里满15以后再添加会继续扩容

    image-20230726214628017

    image-20230726214734859

    跟踪完毕

总结:一般我们是直接调用无参的构造方法的,初始化列表容量的。不会直接取去初始化容量。如果自己初始化容量的话,那么就会调用有参构造方法,按照我们传递的大小去初始化容量,而不是一开始一个空的列表。
别如我们给一个执行容量
在这里插入图片描述
我们后面还是会走这些方法,这些方法都要去判断,好这里进入了一个有参的方法
在这里插入图片描述
这里就初始化为15了
在这里插入图片描述
在这里插入图片描述

执行这个add方法里面肯定还会进行一次装箱的。这里是一样的。
在这里插入图片描述
然后进去这个方法
在这里插入图片描述
这里其实还是1
在这里插入图片描述
然而真正确认容量的我们还是得进去下面的方法。
在这里插入图片描述
这里逻辑是一样的,下面这个if是不成立的,所以不会再重写扩容。这里是一样的。
在这里插入图片描述
但是还是无参构造好吧,谁能预期自己要搞多少数据,自己传参还是多此一举。就这样吧。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兰舟千帆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值