前言
数组的核心使用无非就add,get,foreach等方法,这里只关注于最核心的add方法源码,基于JDK1.8版本。
先看下arraylist的结构。
new的过程做了什么
new ArrayList<>();
这个过程发生了什么? 我们点进去看一下源码
复制代码
这一过程主要是确定elementData的大小。
还有一种new的方法:
这种就是根据传入的集合按顺序组成新数组返回。
可以总结为 arraylist 初始化就是指定数组的大小,没有其他特殊的
add的过程发生了什么
public class TestArrayList {
public static void main(String[] args) {
ArrayList<Object> objects = new ArrayList<>();
objects.add("增加一个元素");
}
}
依然追寻到源码看看如何设计的
复制代码
可以看到首先调用了一个方法: ensureCapacityInternal(size +1 ) 传入的值是当前size加1 也就是 1 ,因为此时size只有默认值。
扫描二维码关注公众号,回复:
13640438 查看本文章
跟进去看看到底做了什么?
哦吼,又得追到参数方法里面看看返回了什么了
这个不难猜出来这一步就是为计算当前得数组最大位置,如果当前数组为空数组,那么返回定义的最小容量也就是10,否则返回传入的值。
接下来回到上一层方法: ensureExplicitCapacity
首先对modCount进行++操作, 可以看作是修改次数增加了1次。
下面的判断: minCapacity 此时为 10 大于数组长度 0 所以这里需要发生扩容。
扩容
看一下扩容的源代码:
重点代码:
int newCapacity = oldCapacity + (oldCapacity >> 1);
这一步就是确认新的数组长度,可以看到是由老的数组长度加上老数组右移1位得到的新长度,也就是每次扩容一半的长度。
但此时oldCapacity为0,进入第一个if判断,将minCapacity的值也就是10赋值给了newCapacity,最后进行扩容。
elementData = Arrays.copyOf(elementData, newCapacity);
数组的扩容复制最终用到了native方法,具体的复制可以自行参考百度,这里不做过多的赘述。
复制代码
复制方法
复制有2种,深复制和浅复制:
- 当数组为一维数组,且元素为基本类型或String类型时,属于深复制,即原数组与新数组的元素不会相互影响
- 当数组为多维数组,或一维数组中的元素为引用类型时,属于浅复制,原数组与新数组的元素引用指向同一个对象
扩容完毕后执行 elementData[size++] = e;
总结
到这应该可以明白几个点:
1. add是线程不安全的。
2. 最好一开始就确认数组的长度,防止频繁发生扩容。
3. 扩容发生在增加元素位置不够时,每次增加1.5倍, 扩容还需要注意深复制和浅复制的问题。
复制代码