一步一步源码探索-ArrayList#add()

前言

数组的核心使用无非就add,get,foreach等方法,这里只关注于最核心的add方法源码,基于JDK1.8版本。

先看下arraylist的结构。

image.png

new的过程做了什么

 new ArrayList<>();  

 这个过程发生了什么?  我们点进去看一下源码

复制代码

image.png

这一过程主要是确定elementData的大小。

还有一种new的方法:

image.png

这种就是根据传入的集合按顺序组成新数组返回。

可以总结为 arraylist 初始化就是指定数组的大小,没有其他特殊的

add的过程发生了什么

public class TestArrayList {

    public static void main(String[] args) {
        ArrayList<Object> objects = new ArrayList<>();

        objects.add("增加一个元素");

    }
}

依然追寻到源码看看如何设计的
复制代码

image.png

可以看到首先调用了一个方法: ensureCapacityInternal(size +1 ) 传入的值是当前size加1 也就是 1 ,因为此时size只有默认值。

跟进去看看到底做了什么?

image.png

哦吼,又得追到参数方法里面看看返回了什么了

image.png 这个不难猜出来这一步就是为计算当前得数组最大位置,如果当前数组为空数组,那么返回定义的最小容量也就是10,否则返回传入的值。

接下来回到上一层方法: ensureExplicitCapacity

image.png

首先对modCount进行++操作, 可以看作是修改次数增加了1次。

下面的判断: minCapacity 此时为 10 大于数组长度 0 所以这里需要发生扩容。

扩容

看一下扩容的源代码:

image.png

重点代码:


int newCapacity = oldCapacity + (oldCapacity >> 1);

这一步就是确认新的数组长度,可以看到是由老的数组长度加上老数组右移1位得到的新长度,也就是每次扩容一半的长度。
        
但此时oldCapacity为0,进入第一个if判断,将minCapacity的值也就是10赋值给了newCapacity,最后进行扩容。
        
elementData = Arrays.copyOf(elementData, newCapacity); 

数组的扩容复制最终用到了native方法,具体的复制可以自行参考百度,这里不做过多的赘述。
复制代码

复制方法

image.png 复制有2种,深复制和浅复制:

  • 当数组为一维数组,且元素为基本类型或String类型时,属于深复制,即原数组与新数组的元素不会相互影响
  • 当数组为多维数组,或一维数组中的元素为引用类型时,属于浅复制,原数组与新数组的元素引用指向同一个对象

扩容完毕后执行 elementData[size++] = e;

总结

到这应该可以明白几个点: 

1. add是线程不安全的。
2. 最好一开始就确认数组的长度,防止频繁发生扩容。
3. 扩容发生在增加元素位置不够时,每次增加1.5倍, 扩容还需要注意深复制和浅复制的问题。
复制代码

おすすめ

転載: juejin.im/post/7049238738026627085