ArrayList扩容分析(jdk1.8)

ArrayList类中的一些成员变量:

private static final int DEFAULT_CAPACITY = 10;//默认初始容量为10
transient Object[] elementData;// 保存ArrayList数据的数组
private int size;//ArrayList 所包含的元素个数
private static final Object[] EMPTY_ELEMENTDATA = {};//空数组(用于空实例)。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//用于默认大小空实例的共享空数组实例。
 //我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多。

                                                                                                  

ArrayList有三个构造函数:下面我们依次来分析

  • 1.指定初始容量的构造函数
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

由代码可知,该构造函数会创建一个大小为initialCapacity的数组

  • 2.无参构造
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

由代码可知,该构造函数会创建一个空数组

  • 3.构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
public ArrayList(Collection<? extends E> c) {
   elementData = c.toArray();
   if ((size = elementData.length) != 0) {
       // c.toArray might (incorrectly) not return Object[] (see 6260652)
       if (elementData.getClass() != Object[].class)
           elementData = Arrays.copyOf(elementData, size, Object[].class);
   } else {
       // replace with empty array.
       this.elementData = EMPTY_ELEMENTDATA;
   }
}
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;
}
public static native void arraycopy(Object src,  int  srcPos,
                                   Object dest, int destPos,
                                   int length);
  • 从下往上依次分析这两个主要个方法

System.arraycopy():将指定源数组中的数组从指定位置复制到目标数组的指定位置。
@ src – 这是源数组 @ srcPos – 这是源数组中的起始位置 @dest – 这是目标数组 @ destPos – 这是目标数据中的起始位置 @ length – 这是一个要复制的数组元素的数目
举例:

int arr1[] = {0,1,2,3,4,5};
int arr2[] = {0,10,20,30,40,50};
System.arraycopy(arr1,0,arr2,1,2);

结果:arr2 = [0,0,1,30,40,50];
Arrays.copyOf() :以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。这个方法在这里的主要作用就是给数组扩容
举例:

 public static void main(String[] args) {
        int[] a = new int[3];
        a[0] = 0;
        a[1] = 1;
        a[2] = 2;
        int[] b = Arrays.copyOf(a, 10);
        System.out.println(b.length);
        for (int i = 0; i < b.length; i++) {
            System.out.print(b[i] + " ");
        }
    }
结果:

10
0 1 2 0 0 0 0 0 0 0

再来分析add()方法

分两种情况讨论:

  • 1.无参构造第一次add
    Arraylist有两个add()重载方法,这里只分析public boolean add(E e)这种情况
 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 void ensureExplicitCapacity(int minCapacity) {
       modCount++;

       // overflow-conscious code
       if (minCapacity - elementData.length > 0)
           grow(minCapacity);
   }
   private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

add(E e)方法中调用了ensureCapacityInternal(size+1)方法,这里传入的参数size+1就是插入当前元素数组所需的最小容量,由于是无参构造,此时传入的参数size+1minCapacity为1,ensureCapacityInternal(size+1)调用了ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))方法,该方法中的calculateCapacity(Object[] elementData, int minCapacity)方法作用是计算数组容量,此时elementData为空所以返回默认容量10,最后ensureExplicitCapacity()中判断当前数组为0小于所需数组容量10,所以进行扩容,也就是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);

所以此时的elementData的长度为0,所以oldCapacitynewCapacity都为0if (newCapacity - minCapacity < 0)条件满足newCapacity为10,最后elementData = Arrays.copyOf(elementData, newCapacity);通过复制组数进行扩容,Arrays.copyOf前面已经分析过。

  • 2.无参构造非第一次add
    add(E e)方法中调用了ensureCapacityInternal(size+1)方法,假设是第二次add此时传入的参数size+1minCapacity为2,ensureCapacityInternal(size+1)调用了ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))方法,此时elementData为1所以返回minCapacity2,最后ensureExplicitCapacity()中判断,当前数组在第一次add时已经扩容为10大于所需数组容量2,所以不进行扩容。只有当第11次add时才会再次扩容,我们分析当前情况下的grow()函数
   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);
    }

此时传入的参数minCapacity为11,oldCapacity = elementData.length;为10,newCapacity扩容为原来的1.5倍.最后通过Arrays.copyOf()方法完成扩容。
`

结论

Arraylist实现无参构造方法时数组为空数组,第一次add()时会将数组扩容为10,直到添加的数组个数大于10时会再次扩容,扩容大小为原来数组的1.5倍,依次类推。
Arraylist实现带初始容量的构造方法时数组大小为用户设置的初始容量,若设置的初始容量小于10,当添加的数组超过用户设置的初始容量会自动扩容为10,若设置的初始容量大于10,则当添加的数组超过用户设置的初始容量会自动扩容为原来的1.5倍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值