ArrayList 扩容机制 源码分析

ArrayList 扩容机制

  1. 创建一个ArrayList时,并不分配空间:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  1. 添加操作:
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
  • 先调用 ensureCapacityInternal(确保内部容量函数)
  • ensureCapacityInternal 内部调用 calculateCapacity
    • 第一次调用,elementData == {} , 得到 minCapacity = 10
    • 第二次调用,elementData = {xx} != {} , 得到 minCapacity = size + 1 = 2
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 
}
  1. 再次调用 ensureExplicitCapacity,判断是否要扩容
    • 第一次进入时:minCapacity - elementData.length = 10 - 0 > 0,进入 grow(10)
    • 第二次添加时:minCapacity - elementData.length = 2 - 10 < 0,不进入 grow()
    • 直到第11次时:minCapacity - elementData.length = 11 - 10 > 0,进入 grow(11)
protected transient int modCount = 0;
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
  1. 第一次今入,调用 grow(10)扩容:

    • 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity = 10。但是第二个if判断不会成立,即 newCapacity不比 MAX_ARRAY_SIZE 大,则不会进入 hugeCapacity 方法。数组容量为10,add 方法中return true , size增为1。

    • 当add第11个元素进入 grow方法时,newCapacity为15,比 minCapacity =11 大,第一个if判断不成立。新容量没有大于数组最大size,不会进入 hugeCapacity 方法。数组容量扩为15,add方法中return true, size 增为11。

    • 以此类推······

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {   //最小需要容量  minCapacity = 10
    // overflow-conscious code
    int oldCapacity = elementData.length;  // old = 0
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // new = 1.5 * old 
    if (newCapacity - minCapacity < 0)    // 1 - 10 < 0
        newCapacity = minCapacity;        // new = 10;
    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);     
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}
  1. 执行完:elementData = Arrays.copyOf(elementData, newCapacity) 后返回 add ()
    • return true
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

Arrays.copyOf()

之前grow()方法中用到的扩容函数:

elementData = Arrays.copyOf(elementData, newCapacity);

toArray() 中也用到了:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

源码:

//Arrays.java
public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

发现 copyOf() 内部实际调用了 System.arraycopy() 方法

System.arraycopy()

add(int index,E element) 中用到的扩容函数:

System.arraycopy(elementData, index, elementData, index + 1,size - index);

源码:

public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

两者联系和区别

联系:

看两者源代码可以发现 copyOf() 内部实际调用了 System.arraycopy() 方法

区别:

arraycopy() 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组,并返回该数组。

ensureCapacity()

这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的

public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

最好在 add 大量元素之前用 ensureCapacity 方法,以减少增量重新分配的次数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ArrayList扩容机制源码如下: ```java private void ensureCapacityInternal(int minCapacity) { // 如果当前容量不足,则需要进行扩容操作 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果当前容量是默认值,则需要将其扩容为默认容量或者minCapacity minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果需要进行扩容,则进行扩容操作 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // 当前容量 int oldCapacity = elementData.length; // 扩容后的容量 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果扩容后的容量仍然小于需要的最小容量,则直接使用需要的最小容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果扩容后的容量超过了ArrayList最大容量,则进行特殊处理 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 创建新的数组,并将原数组中的元素复制到新数组中 elementData = Arrays.copyOf(elementData, newCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } ``` 在上述代码中,`ensureCapacityInternal`方法首先判断当前容量是否足够,如果不足,则调用`ensureExplicitCapacity`方法进行扩容操作。`ensureExplicitCapacity`方法会比较需要的最小容量和当前容量的差值,如果超过了当前容量,则进行扩容操作。`grow`方法是扩容的核心方法,它会首先计算扩容后的容量,然后根据扩容后的容量创建新的数组,并将原数组中的元素复制到新数组中。如果扩容后的容量超过了ArrayList最大容量,则进行特殊处理。`hugeCapacity`方法用于计算需要的最大容量,如果超过了最大容量,则抛出OutOfMemoryError异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值