ArrayList源码解析(非常详细的逐句解读)——JDK1.8

402 阅读4分钟

对于ArrayList,面试中常问的就是初始化和扩容了,以下是我对ArrayList类的三种构造方法、添加元素方法、扩容方法的解读,如有不对,还请大家指出,谢谢!!!

成员变量解析

序列号:

private static final long serialVersionUID = 8683452581122892189L;

初始化时使用无参构造器,第一次调用add()方法所使用的数组容量:

 private static final int DEFAULT_CAPACITY = 10;

初始化时传入参数:0,将用此数组来初始化ArrayList的object数组:

private static final Object[] EMPTY_ELEMENTDATA = {};

初始化时使用无参构造器,将用此数组来初始化ArrayList的object数组:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

ArrayList底层的object数组,transient关键字防止序列化和反序列化:

transient Object[] elementData;

底层数组的元素个数:(注意:与数组容量不同)

 private int size;

方法解析

使用无参构造器初始化数组,和使用有参构造器传入参数new ArrayList(0)来初始化数组时的区别在于: ArrayList对象第一次调用add()方法时,使用无参构造器初始化的对象会把初始容量置为DEFAULT_CAPACITY = 10,而使用参数0初始化的对象会把初始容量置为1。

为了证明上述观点,我们现在来看一下add(E e)方法:

    public boolean add(E e) {
    //调用add()方法时,第一步调用了以下方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!(这句英文是JDK自带的注释,占个坑待会来解释~)
        elementData[size++] = e;
        return true;
    }

为了搞清楚add()方法的执行流程,我们再来看看ensureCapacityInternal (int minCapacity) 方法,以及它调用的一系列方法:

    //流程中执行的第一个方法
    //传入的参数:minCapacity是之前调用add
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //第二个方法
    //在这个方法中设置数组的容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //区别在此处!!!
        //如果数组是用默认空数组初始化的
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //初始化时传入的minCapacity是1,DEFAULT_CAPACITY是16,所以返回的是16,
            //再把这个16传入到第三个方法中作为一个初始容量
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //如果是用空数组初始化的,那么返回minCapacity=1
        //当然了这里还包括不是第一次调用的情况
        return minCapacity;
    }
    //第三个方法
    private void ensureExplicitCapacity(int minCapacity) {
        //这个modCount用来记录数组总共修改的次数(增加、修改、删除)
        modCount++;
        // length是容量,size是元素个数
        //如果这次的add()操作需要的容量已经超过了数组的容量,那么进行扩容。
        //就是说数组已经放不下本次需要新增的元素(一个或多个)了
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

下面贴上三种构造方法的源代码和解读:

//	带初始容量的构造方法
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        //参数为0时,空数组赋值
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

//	无参构造方法
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 {
        // 如果传入的集合对象长度为0,那么用空数组赋值,和传入参数为0一样
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

再讲一下ArrayList的扩容: 上面讲到当调用add()方法添加元素时,如果数组的容量小于本次增加需要的容量,就会进行扩容。

    //将本次add需要的最小容量minCapacity传入扩容方法中
    private void grow(int minCapacity) {
        // overflow-conscious code
        //获取数组旧容量
        int oldCapacity = elementData.length;
        //设置数组新容量为旧容量的1.5倍,
        //(oldCapacity >> 1)这个位运算是 oldCapacity / 2 的意思
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果扩容为旧容量的1.5倍还是不够(可能出现在addAll()方法的情况下),
        //那么将数组容量设置为本次addAll()所需的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果扩容1.5倍后 或者 “本次addAll()所需的容量” 大于规定的最大数组容量MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //那么调用hugeCapacity方法进行处理
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //最后将旧数组的元素复制到新数组来
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

hugeCapacity(int minCapacity)方法解析:

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //在 minCapacity >= 0 的情况下,如果minCapacity大于最大数组容量,
        //那么新数组容量为最大的整型数,反之将其设置为最大数组容量。
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }