集合之ArrayList的底层扩容机制源码分析(详细理解)
1.在进入源码前需要了解到的结论
(1)ArrayList中维护了一个Object类型的数组elementData(transient Object[]Data),其中的transient修饰符表示该属性不能被序列化。
(2)当创建ArrayList对象时,使用的是无参构造,则初始elementData容量为0,第一次加入数据元素后,则扩容element容量为 10,如果需要再次扩容,则扩容elementData容量的1.5倍
(3)如果使用的是指定大小的构造器,则elementData的容量为指定大小,如果需要扩容则直接扩容原来容量的1.5倍。
========开始进入源码分析=
先来一段测试代码:
package com.heyuanhang.conllention_;
import java.util.ArrayList;
/**
- @Author HeYuanHang
- @Date: 2021/4/17 12:57
- @Version 1.8
*/
public class ArrayListYuanMa {
public static void main(String[] args){
//无参构造器,定义一个ArrayList对象时,会初始化一个空数组
//当第一次加入数据元素时会申请一个10个长度的数组
ArrayList list=new ArrayList();
list.add(“heyuanhang”);
}
}
1.首先ArrayList list=new ArrayList();执行这段代码时使用的时无参构造,则底层维护的elementData的容量为0.(由源码可知)
public ArrayList() {
//指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组的定义如下所示
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2.执行list.add(“heyuanhang”);这段代码时,会先判断一下添加这个数据元素后是否超出elementData的容量,如果超出了就需要进行扩容,但是再进行扩容之前会有一个判断的环节,就是判断是先给10个容量(使用无参构造且第一次添加数据时)还是给原本容量的1.5倍(看下源码)
public boolean add(E e) {
//先判断添加一个元素是否超出了之前的容量,当然第一次时这儿的elementData容量为0,肯定超出,然后进入 ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//看一下 ensureCapacityInternal()方法
//int minCapacit这个形参就是elementData数组添加一个1元素后的长度
private void ensureCapacityInternal(int minCapacity) {
//如果elementData是一个{}空数组,则就开始申请10个大小的空间
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY这个值就是10,minCapacity=1
//然后通过Math的max方法取二者的最大值,赋值给minCapacity
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}//其实这个if语句就是判断elementData是否为空的
//然后进入ensureExplicitCapacity方法
ensureExplicitCapacity(minCapacity);
}
//ensureExplicitCapacity方法
//int minCapacity这个形参就是elementData数组添加一个1元素后的长度,如果是经过ensureCapacityInternal方法的if语句后就是10
//当然这儿的值是10
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//这个值就是记录数组被修改的次数
// overflow-conscious code
//如果minCapacity比elementData数组的长度大
//这而才真正的开始扩容
if (minCapacity - elementData.length > 0)
//这个方法才是真正的扩容方法
grow(minCapacity);
}
//看一下grow方法
//int minCapacity这个形参就是elementData数组添加一个1元素后的长度,如果是经过ensureCapacityInternal方法的if语句后就是10
private void grow(int minCapacity) {
// overflow-conscious code
//先把elementData的长度赋值给oldCapacity
int oldCapacity = elementData.length;//测试代码中这儿就是0
//再将oldCapacity与其向右位移1运算的值的和赋值给 newCapacity
//oldCapacity >> 1等价与oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容1.5倍就是这儿体现出来的
//如果newCapacity比minCapacity小的话
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
因此综上所述:ArrayList的底层扩容机制有以下步骤:
1.先判断加入一个元素后是否超过原来的数组容量也就是elementData的容量
2.然后会进入ensureCapacityInternal这个方法开始判断以下刚开始时elementData是否是一个空数组
如果是的话就会将数组的长度标注为10
3.然后会进入ensureExplicitCapacity(int minCapacity)方法,当然再进行真正扩容的时候还会判断一下是否需要进行1.5倍扩容,然后才是调用grow(int minCapacity)进行真正的扩容
A.这儿需要补充一下:
ensureCapacityInternal(size + 1); 该方法的size可以理解成一个跟踪指针,它的值标值ArrayList中有多少个元素了。也就是已经存在多少个元素了。
B.顺便复习一下ArrayList的常用方法
(1)add(Object object):添加元素
(2)add(int index ,Object obj):向指定的index位置开始插入obj
(3)addAll(int dex,Collection):向指定位置dex开始插入一个集合元素
(4)get(int dex):获取指定位置dex处的元素
(5)indexOf(Object obj):返回obj在集合中的首次出现的位置
(6)lastIndexOf(Object obj):返回obj在集合中最后一次出现的位置
(7)remove(int dex):删除指定位置dex处的元素
(8)Object set(int dex,Object obj):将指定位置dex处的元素替换成obj
(9)subList(int st,int end):返回从st到end的子集合。包括st小于end