ArrayList的扩容机制

ArrayList的扩容机制:

当向ArrayList中添加元素的时候,ArrayList的存储容量如果满足新元素的容量要求,则直接存储;ArrayList的存储容量如果不满足新元素的容量要求,ArrayList会增强自身的存储能力,以达到存储新元素的要求。

因为不同的JDK版本的扩容机制可能有差异,下面以JDK1.8为例说明
一、构造方法

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

    /**
     * Constructs an empty list with an initial capacity of ten.
     * ArrayList():并不是在初始化ArrayList的时候就设置初始容量为空,而是在添加第一个元素时,将初始容量设置为10(DEFAULT_CAPACITY)。
     * 在jdk1.6中,无参构造方法的初始容量为10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    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;
        }
    }

二、主要方法
1. 参数说明

    //默认的初始容量
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 当使用构造方法ArrayList(int initialCapacity) 时,若initialCapacity=0,则将ArrayList初始化为EMPTY_ELEMENTDATA
     * 或使用ArrayList(Collection<? extends E> c) ,collection为空时
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 当使用构造方法ArrayList() 时,将ArrayList初始化为空
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * elementData是存储ArrayList元素的数组。
     * ArrayList的容量也就是数组elementData的长度。 
     * 添加第一个元素时,任何elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA(即数组为空,elementData = {})的情况,ArrayList的容量都将扩展为DEFAULT_CAPACITY。
     */
    transient Object[] elementData; // non-private to simplify nested class access


    //The size of the ArrayList (the number of elements it contains).
    private int size;

2. add(E e)方法

    public boolean add(E e) {
        //判断是否可以容纳e,若能,则直接添加在末尾;若不能,则进行扩容,然后再把e添加在末尾
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将e添加到数组末尾
        elementData[size++] = e;
        return true;
    }

每次在add()一个元素时,arraylist都需要对这个list的容量进行一个判断。通过ensureCapacityInternal()方法确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部
3. ensureCapacityInternal方法

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal判断ArrayList默认的元素存储数据是否为空,为空则设置最小要求的存储能力为必要存储的元素和默认存储元素个数的两个数据之间的最大值,然后调用ensureExplicitCapacity方法实现这种最低要求的存储能力
4. ensureExplicitCapacity方法

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

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

若ArrayList已有的存储能力满足最低存储要求,则返回add直接添加元素;如果最低要求的存储能力>ArrayList已有的存储能力,这就表示ArrayList的存储能力不足,因此需要调用 grow();方法进行扩容
5. grow()方法

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); //设置新的存储能力为原来的1.5倍
        if (newCapacity - minCapacity < 0) //扩容之后仍小于最低存储要求minCapacity
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

当ArrayList扩容的时候,首先会设置新的存储能力为原来的1.5倍,如果扩容之后仍小于必要存储要求minCapacity,则取值为minCapacity。
若新的存储能力大于MAX_ARRAY_SIZE,则取值为Integer.MAX_VALUE
在grow方法中,确定ArrayList扩容后的新存储能力后,调用的Arrays.copyof()方法进行对原数组的复制,再通过调用System.arraycopy()方法(native修饰)进行复制,达到扩容的目的
6. hugeCapacity方法

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

从hugeCapacity方法看出,ArrayList最大的存储能力:存储元素的个数为整型的范围。
例:当ArrayList中的当前容量已经为Integer.MAX_VALUE,仍向ArrayList中添加元素时,抛出异常

public class ArrayOrLinked2{
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        
        arrayList.add(1);
        for(int i = 0; i < Integer.MAX_VALUE; i++) //超过ArrayList的最大容量
            arrayList.add(i);
    }
}

ArrayList扩容的例子:
ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。

public class ArrayListTest2 {
    
    public static void main(String[] args) {
    
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        
        arrayList.add(1);
        for(int i = 0; i < 19; i++)
            arrayList.add(i);
    }
}

假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个。


总结
在JDK1.8中,如果通过无参构造的话,初始数组容量为0,当真正对数组进行添加时(即添加第一个元素时),才真正分配容量,默认分配容量为10;当容量不足时(容量为size,添加第size+1个元素时),先判断按照1.5倍(位运算)的比例扩容能否满足最低容量要求,若能,则以1.5倍扩容,否则以最低容量要求进行扩容。

执行add(E e)方法时,先判断ArrayList当前容量是否满足size+1的容量;
在判断是否满足size+1的容量时,先判断ArrayList是否为空,若为空,则先初始化ArrayList初始容量为10,再判断初始容量是否满足最低容量要求;若不为空,则直接判断当前容量是否满足最低容量要求;
若满足最低容量要求,则直接添加;若不满足,则先扩容,再添加。

ArrayList的最大容量为Integer.MAX_VALUE
参考:
https://blog.csdn.net/yang1464657625/article/details/59109133
https://blog.csdn.net/u010176014/article/details/52073339

posted @ 2018-11-13 16:26  zeroingToOne  阅读(14922)  评论(0编辑  收藏  举报