对java中ArrayList底层源码的理解

想要理解ArrayList的底层源码,我们必须Debug观察ArrayList的操作过程

我们先说结论,带着结论看源码更好理解

①ArrayList内部维护着一个Object类型的数组elementData(由于数组是Object类型,Object是所有引用类型的父类,所以该数组可以存储所有的引用类型数据),即ArrayList的本质还是数组

②根据结论①我们知道ArrayList的本质还是一个数组,那么ArrayList和数组的区别是什么?区别是ArrayList存储的数据是可以动态扩容的,即在创建ArrayList集合时不需要指定大小。所以我们看源码更应该关注的是ArrayList内部是怎样实现动态扩容的

③ArrayList常用的构造器有两种,第一种是无参构造器,使用无参构造器则elementData的初始容量为0,在第一次添加数据时,扩容为10,之后每次扩容都是扩容到当前容量的1.5倍。第二种是有参构造器,可以指定elementData的初始容量大小,如果扩容,则直接扩容至当前容量的1.5倍

下面我们进入Debug看源码环节

首先,我们使用无参构造器创建一个ArrayList型变量array,向array中添加十次元素,我们在下面的代码的第一行下个断点,进行Debug

ArrayList array = new ArrayList();
for (int i = 1; i <= 10; i++) {
    array.add(100);
}

可以看到下面的代码,第一行表示DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个Objiect类型的空数组,第二行是ArrayList的构造器,即给elementData赋值为Objiect类型空数组,这就对应着结论①中说的“ArrayList内部维护着一个Object类型的数组elementData”和结论③中说的“使用无参构造器则elementData的初始容量为0”。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

下面进入主程序中的循环阶段,可以看到如下代码,这段代码是将int装箱成Integer类的底层源码,即这段代码把我们要存储的基本数据类型int(100)转化成了Integer类型。为什么呢?这里要回到我们的结论①,结论①告诉我们elementData是Object类型的数组,而Object类型的数组可以存储所有的引用类型数据,但是不能存储基本数据类型,因此需要把基本数据类型int转换成引用类型Integer,所以有了下面的代码

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

此时,int数据已经成功装箱为Integer类型,显然下一步就是把数据装进elementData数组了,但是现在elementData容量为0,那么就来到了我们重点关注的动态扩容阶段,我们可以看到以下代码

注意!!!由于个人才疏学浅,下面的解析写的非常混乱,可以直接看总结

size的初始值为0,所以我们进入ensureCapacityInternal(1);即ensureExplicitCapacity(calculateCapacity(elementData, 1));,于是进入calculateCapacity(elementData, 1);即

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

return Math.max(DEFAULT_CAPACITY, 1);

}

return minCapacity;

由于DEFAULT_CAPACITY值为10,所以return 10;即把10返回给ensureExplicitCapacity。所以执行ensureExplicitCapacity(10);

所以执行

modCount++;

if (10 - elementData.length > 0)

grow(minCapacity);

由于elementData.length==0,故进入grow(10);

oldCapacity=0;newCapacity=0;所以执行newCapacity = minCapacity;即newCapacity=10,然后执行elementData = Arrays.copyOf(elementData, newCapacity);

即elementData = Arrays.copyOf(elementData, 10);

完成第一次扩容

之后一路返回至add方法,执行elementData[size++] = e;

以上是第一次动态扩容的过程,第二次扩容类似

对于这段代码,可以对每个方法的作用做个总结,更好理解

add方法用于添加数据,添加数据时,需要确保数组的容量足够,于是有ensureCapacityInternal方法,用于确保容量足够存储size+1个数据,如果不够,则扩容。那么ensureCapacityInternal方法是怎么实现确保数组容量足够的?minCapacity表示数组需要的最小容量,ensureExplicitCapacity方法用来判断是否需要扩容,但是我们知道无参构造器扩容分两种,所以需要判断该数组是不是无参构造器第一次扩容,因此有calculateCapacity方法用于判断当前的数组是不是由无参构造器创建并且还未扩容,返回给ensureExplicitCapacit数组应该需要的最小容量,然后ensureExplicitCapacit根据minCapacity和elementData.length的值判断是否要扩容,如果需要扩容就执行grow方法进行扩容

private int size;
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    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);
}

总结为:

add方法用于添加数据

ensureCapacityInternal方法用于确保容量足够

ensureExplicitCapacity方法用来判断是否需要扩容,扩容的方法也包含在内

calculateCapacity方法返回给ensureExplicitCapacit数组应该需要的最小容量,以判断是否需要扩容

grow方法用于扩容

猜你喜欢

转载自blog.csdn.net/zaishuiyifang_ct/article/details/131171069