Java ArrayList扩容底层原理深挖

今儿咱来看看ArrayList是怎么扩容的,底层是什么样的

先说结论

1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。

首先咱先看看空参构造

ArrayList al = new ArrayList<>();

//底层源码

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;

elementData是集合中存储对象的数组

DEFAULTCAPACITY_EMPTY_ELEMENTDATA这玩意是一个对象的空数组

然后往里面添加元素

al.add(5);


//底层源码

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

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 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);
    }

public static  T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

这是全部的底层代码,我们分开一点一点看

 

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

size是记录集合中元素个数的成员变量

调用了ensureCapacityInternal方法,传入size+1,

让下一个数组元素等于添加的对象,返回添加成功

 

private void ensureCapacityInternal(int minCapacity) {//size+1当作最小容量
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

calculateCapacity:用在计算数组的容量,它的参数是一个对象数组和一个最小需要的容量。

ensureExplicitCapacity:用在数组容量不足时扩充数组的大小,它的参数是最小需要的容量。

 

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

private static final int DEFAULT_CAPACITY = 10;

DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个常量数组是在ArrayList类中定义的,用于表示一个空的ArrayList。

如果是空的,就返回默认容量(就是10)和最小需要的容量中较大的一个,即Math.max(DEFAULT_CAPACITY, minCapacity)。

人话就是如果集合为空,那就看看所需最小容量和默认容量哪个大,如果默认大就返回10,开辟一个长度为10的数组,如果所需最小容量大,就返回所需最小容量,开辟一个长度为所需最小容量的数组。

那为什么会出现所需最小容量大于默认容量呢?

答案是因为有一个addAll方法可以批量添加数据。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

modCount++,如果所需最小长度大于这个它本身的长度,那就进行扩容。

 

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);
    }

首先计算新的容量,为旧的容量加上一半,即oldCapacity + (oldCapacity >> 1)。这里使用了右移运算符>>,相当于除以2。


然后判断新的容量是否大于等于最小需要的容量,如果不是,就把新的容量设为最小需要的容量。
接着判断新的容量是否超过了数组的最大限制,即MAX_ARRAY_SIZE。如果是,就调用一个叫hugeCapacity的方法,来返回一个合适的容量。


最后,使用Arrays.copyOf方法,把原来的数组复制到一个新的数组中,并把新的数组赋值给elementData。

总结一下吧,ArrayList扩容底层大概分为四步:

1.利用空参构造创建集合时,在底层创建一个默认长度为0的数组。
2.添加第一个元素时,底层会创建一个新的长度为10的数组,要是存不下,就创建一个能正好存下的数组。
3.这个数组存满时,会扩容1.5倍创建新数组,并把旧数组拷贝到新数组中。
4.如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准,并把旧数组拷贝到新数组中。

你可能感兴趣的:(在Java的底层挖呀挖呀挖,java,开发语言)