只知道在ArrayList 添加一个元素在尾部添加元素,如果容量不够就会扩容1.5倍,也没有通过源码去研究过这个过程。今天我们就来研究研究:
中间插入,和末尾插入 这两种方式 来进行研究。

尾部添加

首先我们实现add方法

@Test
public void testEndAdd(){
    ArrayList arr = new ArrayList();
    arr.add(1);
    arr.add(1);
}

点进add方法我们可以看到add里面有一个ensureCapacityInternal方法,并且传了一个size(list里面数据的长度)+1的参数到这个方法里面

elementData 是Arraylist中存放元素的真正数组,size是当前数组中存了几个元素,而不是数组的长度!!!

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

点进ensureCapacityInternal方法,我们可以看到我们传进来的size+1是minCapacity这个参数,这个参数就是加入一个元素后元素的个数

private void ensureCapacityInternal(int minCapacity) {
	//判断数组是不是空,我们第一次的添加是空,minCapacity就变成了DEFAULT_CAPACITY--> 10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //确定是否需要扩容,下一步进入
    ensureExplicitCapacity(minCapacity);
}

点击进入ensureExplicitCapacity 我们首先看到是一个modCount++ 并且在ArrayList,LinkedList,HashMap等等的内部实现增,删,改中我们总能看到modCount的身影,modCount字面意思就是修改次数所有使用modCount属性的全是线程不安全的,而且只有在本数据结构对应迭代器中才使用。 这里我不深究,大家可以参考这篇文章

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

        // overflow-conscious code
        //判断minCapacity(元素的个数)是否大于  elementData.length(数组的长度),这里因为是初始化所以是10,其他情况不一样
        //进入grow方法
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//由于是第一次添加oldCapacity = 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//这里是扩容到的1.5倍,所以newCapacity = 0
		//情况一:
        //如果扩容后的长度还是小于当前需要存入的个数minCapacity ,为扩容为minCapacity 的大小
        if (newCapacity - minCapacity < 0)//判断是否大于0,这里是 0-10 < 0
            newCapacity = minCapacity;//newCapacity  = 10

		//情况二:
		//判断传入的参数minCapacity是否大于MAX_ARRAY_SIZE,如果minCapacity大于MAX_ARRAY_SIZE,返回Integer.MAX_VALUE,否者返回MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
           
        //这个Arrays.copyOf()我们很熟悉,程序走到这里就代表老老实实的扩容了。将原来的数据也复制在这个数组中。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

这里我们也不深究这个还与jvm储存头部字有关系,而且我们一般也用不到这么大的空间,有兴趣看这篇文章

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

到下一步,这个代码看起就有点打脑壳,意思就是说先判断这个数组的是不是Object类型的,如果是直接创建一个的Object的数组Object[newLength] 数组,这个newLength就是我们需要扩容后的大小,如果不是Object的就直接new一个目标类型,长度也是newLength

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        //最终目的就是保证传入给System.arraycopy() 方法中参数"copy" 的值是一个我们扩容后的新数组
        System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
        return copy;
    }
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //把添加的元素放入数组中已存入元素个数的+1位置!
    elementData[size++] = e;
    return true;
}

自此完成了尾部的添加

中部插入

插入代码

@Test
public void testMidAdd(){
    ArrayList arr = new ArrayList();
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(3,2);
}

进入add方法

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 ensureCapacityInternal(int minCapacity) {
	//判断数组是否为空,这里不为空
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
	//直接跳到这一步
    ensureExplicitCapacity(minCapacity);//minCapacity = 6
}

进入ensureExplicitCapacityif (minCapacity - elementData.length > 0)这个是判断minCapacity (元素个数)与elementData.length(数组长度)的大小关系,如果元素个数大于数组长度就会进入grow方法进行扩容,这里面就与上面尾部添加的流程一样了。这里minCapacity (元素个数)- elementData.length(数组长度)= 6 - 10 < 0,所以就不进入grow方法。

private void ensureExplicitCapacity(int minCapacity) {
	//修改次数+1
    modCount++;

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

然后就会回到add方法 进行System.arraycopy

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++;
}

核心语句

如果不需要扩容的话(需要扩容的话就先扩容),系统会将index下标以后的元素,统一向后移动一位,也就是把elementData中从index开始以后的元素,复制到elementDataindex+1的位置,复制个数呢,就是index以后的所有元素,也就是size-index个,这里源数组和目标数组一样。

System.arraycopy(elementData, index, elementData, index + 1,size - index);

我们可以点进去看一下每个参数的含义:

public static native void arraycopy(Object src, int  srcPos, Object dest, int destPos, int length);

五个参数分别为
src:源数组
srcPos:从源数组的下标开始复制
dest:目标数组
destPos:目标数组的下标开始
length:复制多少个元素

我们插入的是这段代码,我们对其进行分析

@Test
public void testMidAdd(){
    ArrayList arr = new ArrayList();
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(1);
    arr.add(3,2);
}

这里就是 从源数组elementData以index作为起始位置,截取长度为size - index:5 - 3(size是以0开头的),截取了三个数。截取到的新数组,向目标数组elementDataindex+1:4 ,为起始位置进行复制添加。刚刚就中间就空出了一个索引为index:3 的位置,然后在执行下面两句话,将指定元素赋值到索引位置,然后size+1 ,就完成了元素从中间插入的过程。

elementData[index] = element;
size++;

执行结果:

arraylist添加元素进去 java arraylist中间添加一个_ArrayList