Java源码阅读系列(一)-ArrayList源码阅读 1、增加元素

ArrayList源码分析

1、增加元素

ArrayList有两个不同的add()方法。常用的就是第一个,添加元素到list的末尾,只分析第一个方法。

/**
     * 将指定的元素添加到列表末尾.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        //确认List容量
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //把元素放到List的末尾
        elementData[size++] = e;
        return true;
    }

来看ensureCapacityInternal(size + 1); // Increments modCount!!

执行的是以下方法,size是当前ArrayList的大小0:

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  //calculateCapacity执行的是下面的静态方法calculateCapacity
}

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

calculateCapacity方法中对elementData进行检查,如果它是DEFAULTCAPACITY_EMPTY_ELEMENTDATA的,就返回size和DEFAULT_CAPACITY两者中的最大值,第一次添加元素时,扩容到默认值10。

调用无参构造的时候,把DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给了 elementData,此时一定是它。

/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

DEFAULT_CAPACITY默认为10.

/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

源码中对elementData的注释写的很清楚,如果 一个ArrayListelementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA,那么在第一次添加元素时就扩容为默认容量10.

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

下面这一步就是很重要的扩容了,calculateCapacity返回ArrayList的大小之后,执行ensureExplicitCapacitymodCount++,然后判断元素的lengthminCapacity的大小,在第一次增加元素的时候,会进入grow

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

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

扩充:

/**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
private void grow(int minCapacity) {
    // 获取当前数组容量
    int oldCapacity = elementData.length;
    //扩容,新数组容量 = 当前数组容量 + 当前数组容量/2
    //右移除以2的几次幂,左移乘以2的几次幂
    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);
}

大容量分配:

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    //判断想要的是否大于临界值,如果大于,则返回Integer.MAX_VALUE(0x7fffffff),否则返回MAX_ARRAY_SIZE
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

MAX_ARRAY_SIZE为什么要-8

因为某些VM会在数组中保留一些头字,尝试分配这个最大存储容量,可能会导致Array容量大于VMlimit,最终导致OutOfMemoryError

/**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

然后增加的过程就完事了。

未完待续。。。
很快!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章