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);
}
}
- 1 定义列表的时候没有指定初始化容量,那么就会默认调用它的无参构造器,这里初始化了数组,是一个空的数组
可以看到这里是一个Object类型的数组,但是是一个空的。
这里断点返回会回到这个初始化的方法这里,可以看到初始化列表为一个空的列表
下面继续执行
- 2 执行add方法来观察ArrayList的扩容机制
首先执行10次添加元素
进入方法,来到这里,其实在真正添加元素之前会执行装箱的操作,也就是Integer 的ValueOf方法
从这里我们可以看出 :
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的
然后进入到这个方法,这个方法字面意思理解就是确认容量的操作,首先第一句判断肯定是符合的,因为我们调用无参的构造方法的时候就是这句话初始化的。
所以这里这个最小的容量会被初始化为10
下面还有一段代码进行传入将10这个值传入
进去方法
然后执行下面这个方法,肯定是要执行grow的,这个其实可以理解为第一次扩容
然后进入grow方法
运行
运行到这里
这里进行了一个数组拷贝的操作其实就是扩容了在首次添加元素的时候
然后回到这里,这里进行了元素的赋值
在idea中要想看到这个完整的扩容还需要这里操作下,就是断点调试后在这个variables这里进行右键选择这里
没有出现扩容效果的话可以这样设置然后再断点
然后再看
这里效果就出来了,所以这里会到10容量首次
-
3然后继续添加,我们找到边界10。
然后继续添加,这里就是走到下一个for循环,走到这里
min变11
传入
传入
这里首先new变15,第一个if肯定是没有执行的,这样这个newCapcticy就是15了,最后到了那个数组拷贝
说明一下这个huge,其实一般是不会走到这里的,我使者循环1了添加这么多元素,实在太卡了,所以如果添加这么多元素性能会大大降低。
变15容量
然后继续添加的话,可以看到这里满15以后再添加会继续扩容
跟踪完毕
总结:一般我们是直接调用无参的构造方法的,初始化列表容量的。不会直接取去初始化容量。如果自己初始化容量的话,那么就会调用有参构造方法,按照我们传递的大小去初始化容量,而不是一开始一个空的列表。
别如我们给一个执行容量
我们后面还是会走这些方法,这些方法都要去判断,好这里进入了一个有参的方法
这里就初始化为15了
执行这个add方法里面肯定还会进行一次装箱的。这里是一样的。
然后进去这个方法
这里其实还是1
然而真正确认容量的我们还是得进去下面的方法。
这里逻辑是一样的,下面这个if是不成立的,所以不会再重写扩容。这里是一样的。
但是还是无参构造好吧,谁能预期自己要搞多少数据,自己传参还是多此一举。就这样吧。