# ArrayList源码阅读

# 📝 ArrayList源码分析

ArrayList 是代码中使用非常频繁的,所以看底层的代码时非常有必须的.

# 数据结构和参数

DEFAULT_CAPACITY : 默认集合大小;默认值是10

EMPTY_ELEMENTDATA : 空的Object数组,用于后续的初始化和赋值

DEFAULTCAPACTITY_EMPTY_ELEMENTDATA: 默认空的Object数组,也是用于后续的判断和赋值

elementData: 存放ArrayList数据的数组

size: 记录ArrayList中元素的个数;size不一定是elementData的长度

ArrayList 是一个由 Object [] 的数组来实现的;transient Object[] elementData ,这个变量就是存放数据的

长度是用 size 这个变量来记录的,而不是直接调用的数组的长度

如果ArrayList list = new ArrayList(); 只是仅仅new一个集合的话,数组的大小是没有初始化为10的,而是在add()中,进行判断。 如果数组的是为空的数组的话,就会使用 DEFAULT_CAPACITY 来进行初始化。也就是要调用add方法才行

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    return Math.max(DEFAULT_CAPACITY, minCapacity);
}

# 方法

# add方法

add 里面是走了三个方法, size 没有赋值的情况下,就是0

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

// 确认容量 , 打个比方我们没有对size进行赋值,那么size + 1 传入到这个里面的值也就是1,那么 elementData 对应的也就是一个空数组
private void ensureCapacityInternal(int minCapacity) {
     ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

//  满足是空数组的话,就会使用默认的值 10 于 minCapcacity 来进行对比,这里返回的10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

//  如果 minCapacity  减去 数组的长度是大于0的,就会调用grow来进行扩容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

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

// 这里可以看到先对数组的值进行,然后对保存出来的值进行1.5倍扩容,与传入进来的值进行对比,满足条件赋值.这里就要看到 Arrays.copyOf(elementDate,newCapacity); 这才是真正的对数组进行扩容的方法,也就是直接调用Arrays的API. Arrays.copyOf() 里面最后也是调用了 System.arraycopy()的方法
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }
到这里 ensureCapacityInternal 方法也就是走完了
---------------------------------------------
后面就是使用 数组下标来进行赋值并且返回true

所以add方法的总体流程如下

add方法

# add根据index添加

在上述的add逻辑之前,进行index是否符合规则的校验

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

// 检查下标是否越界 , ensureCapacityInternal 方法和上面一样
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

//  System.arraycopy() 从 elementDate 的 index处开始复制, 复制给后面的elementDate数组的值,从index + 1 开始复制,也就是说 index 相当于修改了 index + 1, 然后index位置就是没有值了,所以elementDate[index] = element的值,size ++.

根据下标添加元素的逻辑流程图

add根据index下标

# set方法

根据下标来对久的值进行一种替换,取出对应下标的值,然后下标对应的位置赋值给新值,最后返回旧值回去即可

// 先检查下标是否越界,如果越界就会抛出异常
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

// 取出对应下标的值
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

set方法逻辑

set方法逻辑

# remove方法

根据传入进来的值进行删除

// 分为 null 和 不是 null 的情况来进行删除.满足条件的话,最后都会调用到 fastRemove方法中来
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

//  根据传入进来的 下标来删除数据,System.arraycopy 这个方法并不默认,根据下标的位置来进行复制数组。
//  可以看到最后有一个 将值设置为null的操作,从注释上看是help GC, 帮助GC
private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

# remove根据下标删除

可以看到根据下标删除的话,会先判断传入进来的下标是否满足条件,就是没有出现越界的情况

然后取出旧值,接下来的代码就是非常的熟悉了,就是fastRemove() 里面的代码了

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

根据下标移除元素逻辑

remove方法逻辑

# 🤗 总结归纳

大致就是看 ArrayList 是如何添加数据的,对数据是怎么保存的,是如何删除数据的,是怎么样进行扩容的,大致弄明白这些就是对ArrayList有一个大致的了解

# 📎 参考文章