ArrayList自动扩容机制

扩充容量的方法ensureCapacity。

ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容量minCapacity),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList。

// 确定ArrarList的容量。  
    // 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”  
    public void ensureCapacity(int minCapacity) {  
        // 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的  
        modCount++;  
        int oldCapacity = elementData.length;  
        // 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1”  
        if (minCapacity > oldCapacity) {  
            Object oldData[] = elementData;  
            int newCapacity = (oldCapacity * 3)/2 + 1;  
			//如果还不够,则直接将minCapacity设置为当前容量
            if (newCapacity < minCapacity)  
                newCapacity = minCapacity;  
            elementData = Arrays.copyOf(elementData, newCapacity);  
        }  
    }  
 // 添加元素e  
    public boolean add(E e) {  
        // 确定ArrayList的容量大小  
        ensureCapacity(size + 1);  // Increments modCount!!  
        // 添加e到ArrayList中  
        elementData[size++] = e;  
        return true;  
    }  

我们可以发现,在add方法中,每次添加元素,都会调用ensureCapacity方法来判断是否需要扩容,并且传入(size+1)这个参数,size参数为现在的ArrayList的容量,+1表明如果成功插入,容量将变为多少。也可以理解成:正确插入需要的最小容量minCapacity,我们可以看到ensureCapacity中传入的正是这个参数。

ensureCapacity方法中,首先判断oldCapacity(当前ArrayList容量)是否大于minCapacity,如果大于则说明容量足够,不做任何操作,跳出函数。若当前容量不足以容纳当前的元素个数,则设置 新的容量=“(原始容量x3)/2 + 1”,如果还不够,则直接将minCapacity设置为当前容量(保证这一次正好可以正确插入,下次再插入还得扩容。。)

最后调用Arrays.copyOf方法,传入旧的需要扩容的ArrayList 和 新的容量。

Arrays.copyof()方法

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

很明显调用了另一个copyof方法,该方法有三个参数,最后一个参数指明要转换的数据的类型,其源码如下:

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        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;
    }

 这里可以很明显地看出,该方法实际上是在其内部又创建了一个长度为newlength的数组,调用System.arraycopy()方法,将原来数组中的元素复制到了新的数组中。

    下面来看System.arraycopy()方法。该方法被标记了native,调用了系统的C/C++代码,在JDK中是看不到的,但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数,因此它可以保证同一个数组内元素的正确复制和移动,比一般的复制方法的实现效率要高很多,很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法,以取得更高的效率。

总结: 

ArrayList查找速度确实快,但是频繁的插入删除数据对于ArrayList来说效率就很低了,需要不停的复制、移动元素。

猜你喜欢

转载自blog.csdn.net/Fury97/article/details/81080997