年轻人不讲武德,一起聊聊List集合

一、List类图
年轻人不讲武德,一起聊聊List集合_第1张图片
二、源码剖析

  1. ArrayList(此篇详解)

构造函数
// 默认值-空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// ArrayList底层为数组 transient关键字:当前数组不能被序列化
transient Object[] elementData; 

/**
 * 构造函数
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

从源码中可以看出,构造只为底层数组进行初始化,默认值为空数组;

add() - 添加元素方法
// ArrayList元素个数
private int size;

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

// 记录对ArrayList操作次数
protected transient int modCount = 0;

// ArrayList最大元素个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * 添加入口
 */
public boolean add(E e) {
    // 对数组进行初始化以及扩容
    ensureCapacityInternal(size + 1);
    // 数组中添加元素
    elementData[size++] = e;
    return true;
}

// 1.对数组进行初始化以及扩容
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 2.计算最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 判断数组是否为空,即为第一次添加元素
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // max方法:a >= b ? a : b
        // eg: 10 >= 1 -> 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

// 3.判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
    // modCount(记录修改次数) -> 在当前线程使用迭代器的过程中,如有其他线程修改了modCount(会判断是否修改操作有误(线程安全问题)) -> 抛出ConcurrentModificationException异常
    //      Fail-Fast 机制,快速失败机制,modCount声明为volatile,保证线程之间修改的可见性
    modCount++;

    // 判断是否进行扩容
    if (minCapacity - elementData.length > 0)
        // 具体扩容方法
        grow(minCapacity);
}

// 4.具体扩容
private void grow(int minCapacity) {
    // 获取数据长度
    int oldCapacity = elementData.length;
    // 计算扩容后长度,第一次为例:0 + (0 >> 1) = 0 + 0 = 0
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 判断如果扩容后长度-最小容量<0,扩容后的长度为最小容量,此判断作用于第一次添加元素时
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 计算扩容后长度最大值,最大值为Integer的最大值(2^31-1)
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

    // 使用 Arrays.copyOf 对我们数组容量实现扩容
    elementData = Arrays.copyOf(elementData, newCapacity);
}

从源码中可以看出,添加元素时且会对数组进行初始化或者扩容;

知识点:
第一次扩容也就是第一次add时:数组长度length扩容为10,集合size为1;
第二次扩容也就是第十一次add时:数组长度length扩容为15,集合size为11;
之后每次扩容遵循以下规则,oldCapacity + (oldCapacity >> 1);

结论:
每扩容后为之前数组长度的1.5倍,最大值:Integer最大值(2^31-1),最小值:10;
get() - 获取元素方法

public E get(int index) {
    // 校验是否越界
    rangeCheck(index);

    // so easy:通过下标获取元素
    return elementData(index);
}

// 校验下标是否越界
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}

从源码中可以看出,获取元素时就是获取数组元素,通过下标直接获取即可
remove() - 删除元素方法
/**
* 入口
*/
public E remove(int index) {
// 校验下标是否越界
rangeCheck(index);

    // add()方法中已详情描述
    modCount++;

    // 获取要删除的元素
    E oldValue = elementData(index);

    /**
     * public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
     *  说明此方法参数作用:
     *      src:源数组
     *      srcPos:源数组复制的起始位置
     *      dest:目的数组
     *      destPos:目标数组放置的起始位置
     *      length:数组元素被复制的数量
     */
    // 对应参数中length
    int numMoved = size - index - 1;
    // 删除元素其实就是一个数组整体移动的过程,再将最后一个元素置空即可
    if (numMoved > 0)
        // 此每个参数都需各位猿友细品下,慢慢来,只是一个过程... 如此如此,这般这般,暖男的我在下方提供图,便于猿友们理解
        System.arraycopy(elementData, index+1, elementData, index, numMoved);

    // 将最后一个元素置空,如只有一个元素,置空即可,便于GC工作
    elementData[--size] = null; // clear to let GC do its work

    // 返回删除的元素值
    return oldValue;
}

从源码中可以看出,删除元素实则为数组移动覆盖的过程,已下图为例,便于大家理解:

源数组:
年轻人不讲武德,一起聊聊List集合_第2张图片
目标数组(删除元素后的数组):
年轻人不讲武德,一起聊聊List集合_第3张图片
删除下标为0的元素(不)
结合 arraycopy(Object src, int srcPos, Object dest, int destPos, int length)来讲,可得知:

src:为上述源数组;
srcPos:源数组要复制的起始位置为(index+1 = 0+1 = 1)
dest:为上述目标数组
destPos:目标数组放置的起始位置为(index=0);
length:复制的长度为(size-index-1 = 4-0-1 = 3)
过程演示:
年轻人不讲武德,一起聊聊List集合_第4张图片
ArrayList总结:
底层为数组;
构造初始化,数组为空数组,集合size为0,数组length为0;
第一次扩容也就是第一次add时:数组长度length扩容为10,集合size为1;
第二次扩容也就是第11次add时:数组长度length扩容为15,集合size为11;
之后每次扩容遵循以下规则,oldCapacity + (oldCapacity >> 1),故每次扩容为之前数组长度的1.5倍;
最大值:Integer最大值2147483647(2^31-1),最小值:10;
通过下标去获取元素,故查询效率高,增删效率低;
线程不安全;
有modCount;

你可能感兴趣的:(年轻人不讲武德,一起聊聊List集合)