ArrayList核心源码解析

90 阅读3分钟

1.常量数据

/**
 * 默认容量大小
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 空数组
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 默认空数组
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存放数据元素的数组    
 */
transient Object[] elementData; // non-private to simplify nested class access

2.构造器

/**
 * 带初始化容量的构造器
 */
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);
    }
}

/**
 * 不带初始化容量的构造器(在初始化的时候并没有指定大小,而是给了一个空数组
    ,只有在第一次add的时候才会扩容为10,详细见下面的扩容分析)
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 初始化的时候就赋值构造器
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

3.扩容机制分析

/**
 * 要分配的最大数组大小
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

/**
 * 用于处理没有指定容量就初始化的情况,当没有指定容量初始化的时候就会在这里给一个默认大小10
 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

/**
 * 判断是否需要扩容
 *  minCapacity:需要的容量大小
 */
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 如果需要的容量比当前数组的长度要长,这时就需要进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 * 扩容的核心方法
 */
private void grow(int minCapacity) {
    // 得到当前数组的长度
    int oldCapacity = elementData.length;
    
    // 按照1.5倍左右进行扩容,如果oldCapacity是偶数则是1.5倍,否则是1.5左右
    // >> 1 相当于除2,此位运算相当于除以2的幂次方,这里为什么不用算术运算,
    // 因为位运算的性能要比算术运算好很多,只需要移动位置即可
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // 如果扩容以后的长度还是比所需要的长度小,就将所需要的长度给扩容的长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;、
    
    // 如果扩容以后的长度大于分配数组的最大长度,防止造成内存泄露,将最大容量设置Integer.MAX_VALUE或者为MAX_ARRAY_SIZE
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    
    // 根据扩容长度进行扩容
    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;
}

4.add方法

/**
 * 添加元素
 */
public boolean add(E e) {
    // 判断是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将元素放入数组中
    elementData[size++] = e;
    return true;
}

通过上面我们可以知道,当没有指定初始化容量实例化对象的时候,首先会给一个空数组,只有在add第一个元素的时候才会扩容为10,现在来看一下整个流程

1.当添加第一个元素的时候,进入到calculateCapacity方法的以后,此时会返回DEFAULT_CAPACITY
2.紧接着进入到ensureExplicitCapacity中,判读成立,进行扩容
3.当第二个元素进去的时候,minCapacity为2,再次进入到ensureExplicitCapacity时,2-10 > 0 不成立,所以不会进行扩容
4.直到第11个元素进去的时候,此时minCapacity为11,再次进入到ensureExplicitCapacity的时候11-10成立,进行扩容,扩容得到的新值为10 + 10/2 = 15

5.注意事项

1.为什么明明默认初始化是10,还要在初始化的时候去指定长度呢?
因为默认初始化的时候只会指定为空数组,并不会指定容量的大小,只有在第一个元素进去的时候才会进行扩容,而直接指定初始化大小,就不需要做哪些扩容操作,性能肯定是更好的。