ArrayList扩容源码剖析
Posted 爱上口袋的天空
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList扩容源码剖析相关的知识,希望对你有一定的参考价值。
一、先看ArrayList的简单总结
1、ArrayList中维护了一个
Object类型的数组
elementData.transient Object[] elementData;(Object类型的数组说明什么类型都可以放)
对象在序列化的时候,transient修饰的属性不会被序列化
2、当创建对象时,如果使用的是无参构造器,则初始elementData容量为0,其实就是一个空数组
3、当添加元素时:先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置
4、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
5、如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity
6、如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍。
二、底层源码解析1
1、我们使用下面的代码程序进行源码debug分析
package com.kgf.kgfjavalearning2021.collection; import java.util.ArrayList; public class ArrayListSource public static void main(String[] args) //使用无参构造创建ArrayList对象 ArrayList arrayList = new ArrayList(); //使用for循环为list添加1-10数据 for (int i = 0; i < 10; i++) arrayList.add(i); //使用for循环为list添加11-15数据 for (int i = 11; i <= 15; i++) arrayList.add(i); arrayList.add(100); arrayList.add(200); arrayList.add(null);
2、首先进行下面创建无参构造器的源码分析
进入内部源码查看:
3、当第一次向ArrayList集合中添加元素时
点击进入add方法:
下面我们进入ensureCapacityInternal方法,这个方法是用来确认底层数组大小是否够用来存放数据,如果不够就需要进行扩容:
可以发现上面会进行判断数组是不是一个空数组,因为第一次无参构造创建ArrayList集合底层就是一个空数组,所以如果发现是一个空数组,那么扩容elementData大小为10
下面还需要进入ensureExplicitCapacity方法去判断是否需要真的扩容:
上面的含义就是ArrayList是空间不够了才扩容的,假设现在插入第11个元素,但是只有10个空间,就触发grow扩容
4、下面分析grow扩容方法
上面的代码逻辑分析:
1)、首先获取elementData数组的长度oldCapacity
2)、oldCapacity >> 1表示就是oldCapacity除以2,所以newCapacity就等于oldCapacity
的1.5倍3)、但是第一次的时候oldCapacity等于0,所以不会走这个1.5倍的扩容机制
4)、判断newCapacity是否大于MAX_ARRAY_SIZE,如果大于走hugeCapacity方法
下面进入hugeCapacity方法看看:
可以发现最大就是Integer.MAX_VALUE
5)、最后就是真正的开始扩容了
并且注意:Arrays.copyOf这个方法在扩容的时候会保留原来的数据,例如:
String[] arra = ["1","2"];
使用Arrays.copyOf扩容到5个,结果如下:
String[] arra = ["1","2",null,null,null];
5、最后回到add方法进行添加元素,因为此时经过扩容,大小已经够了
注意,注意,注意,Idea 默认情况下,Debug 显示的数据是简化后的(就是看不到全部数据),如果希望看到完整的数据需要做以下设置.
三、底层源码解析2
1、下面我们把上面的测试代码程序设置一个初始化集合大小
package com.kgf.kgfjavalearning2021.collection; import java.util.ArrayList; public class ArrayListSource public static void main(String[] args) //使用无参构造创建ArrayList对象 ArrayList arrayList = new ArrayList(8); //使用for循环为list添加1-10数据 for (int i = 1; i < 11; i++) arrayList.add(i); //使用for循环为list添加11-15数据 for (int i = 11; i <= 15; i++) arrayList.add(i); arrayList.add(100); arrayList.add(200); arrayList.add(null);
2、下面我们进行debug查看源码
创建ArrayList对象完成,我们看一下第一次添加元素时流程:
下面我们看一下当添加的元素超过8个的时候扩容方式:
以上是关于ArrayList扩容源码剖析的主要内容,如果未能解决你的问题,请参考以下文章