【源码解析】扒开ArrayList的外衣

积千里跬步,汇万里江河;天天进步一点点,终有一天将成大佬。

本文内容

固然ArrayList里的方法不止这些,本文主要讲一些经常使用的方法java

方法变量

Arraylist里的方法变量主要有如下几个数组

1. 构造方法

1.1 有参构造

1.1.1 传入数组的大小

1.1.1.1代码实现
List<String> list=new ArrayList<>(5);
1.1.1.2源码解析

1.1.2 传入一个list对象

其实这个就至关于把传入的list对象里的数据复制到新的ArrayList对象工具

1.1.2.1 代码实现
List<String> list=new ArrayList<>(Arrays.asList("z","m","h"));
这里用来 Arrays工具类里的 asList方法,它的源码里是直接返回一个List,有兴趣的能够去看看,这里就不介绍了
1.1.2.2 源码解析

1.2 无参构造

这个比较简单,直接赋值一个空数组源码分析

1.2.1代码实现

List<String> list=new ArrayList<>();

1.2.2源码解析

2. add方法

add通常经常使用的有两个方法,一个就是add(E e)在尾部添加数据,一个就是add(int index,E element)在指定位置插入元素性能

2.1 add(E e)

这个是Arrayist的主要方法,平时用的也是最多的方法之一,因此源码比较复杂,比较长spa

2.1.1 代码实现

List<String> list=new ArrayList<>();
list.add("灰灰HK");

2.1.2 源码解析

  • ensureCapacityInternal(int minCapacity)确保数组容量充足

  • calculateCapacity(Object[] elementData, int minCapacity)

  • 再回到ensureExplicitCapacity(int minCapacity)这个方法,这个方法先修改次数加1,而后判断size+1是否是比当前的数组容量大,若是比当前的数组容量大,则进行扩容操做,扩大容量为原数组的1.5倍
好比第二次调用add方法,此时 size+1=2, elementData.length=10,为何等于10呢?由于第一次默认把数组容量从0扩大到了10,这时 size+1elementData.length小,就不会进行扩容操做

  • grow(int minCapacity)扩容
这里调用 Arrays.copyOf()方法进行复制操做,当进一步深刻这个方法时,发现是由 System.arraycopy()这个方法实现复制功能的,这个方法由 native关键字修饰,表示不是由 Java语言实现的,通常是c/cpp实现

2.1.3 小结

到这里,add的方法流程就走完了,其核心步骤:3d

  • 每次添加元素时判断数组容量是否充足
  • 第一次添加元素,把数组容量扩容到10
  • 扩容时,除第一次,之后的每次扩容为原大小的1.5倍
  • 扩容后调用System.arraycopy()方法把原数组的元素复制到扩容后的新数组

2.2 add(int index, E element)

该方法为在指定位置插入元素,该位置及后面全部元素后移code

2.2.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.add(0,"灰灰");

2.2.2 源码解析

能够看到,这边又用到了 System.arraycopy()这个方法
  • rangeCheckForAdd(int index)判断是否越界
这里他是和 size对比,而不是和数组的 length对比,我我的认为这样第一节省了空间,第二方便后面移动的操做

  • System.arraycopy()拷贝数组
public static native void arraycopy(Object src,  int  srcPos,
                                     Object dest, int destPos,
                                    int length)
  • src 原数组对象
  • srcPos 原数组起始位置
  • dest 目标数组
  • destPos 目标数组起始位置
  • length 复制多少个数据

2.2.3 小结

插入方法其主要步骤以下:对象

  • 检查插入的位置是否越界
  • 检查数组容量是否充足,不充足进行扩容相关操做
  • 调用System.arraycopy()进行index及后面的元素后移

3. get方法

3.1 get(int index)

3.1.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.get(0);

3.1.2 源码解析

  • rangeCheck(int index)判断是否越界
get个add方法判断越界的方法是不同的,这边是 index>=size,多了个 等于,为何要多个等于呢?由于数组是从0开始的,而size 至关于是开始的从1开始的
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
  • elementData(int index)直接返回对应下标的数组元素
E elementData(int index) {
    return (E) elementData[index];
}

3.1.3 小结

get方法比较简单,主要步骤为:blog

  • 检查是否越界
  • 返回对应元素

4. set方法

4.1 set(int index, E element)

4.1.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.set(0,"灰灰");

4.1.2 源码解析

5. remove方法

5.1 remove(int index)

5.1.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.remove(0);

5.1.2 源码解析

当删除的元素为最后一个元素时, numMoved就小于0了,就不会进行移动元素的操做

5.2 remove(Object o)

这个方法在实际中用的比较少,由于 AraryList是能够保存重复的元素,因此删除是 删除最先添加的元素

5.2.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.remove("hk");

5.2.2 源码解析

  • fastRemove(int index)删除元素
这个方法和remove(int index)内部的操做相似,不过这边不保存被删除的元素
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
}

6. clear方法

6.1 clear()

6.1.1 代码实现

List<String> list=new ArrayList<>();
list.add("hk");
list.clear();

6.1.2 源码分析

总结

ArrayList底层扩容或者移动数组元素时都调用了System.arraycopy()来进行相关操做,平时进行咱们进行数组复制或移动的时候也能够调用这个方法了,这个性能比循环复制性能高多了,特别是在大量数据的时候。

文章好几回出现了modCount++这个操做,这个modCount主要用户内部类的迭代器

相关文章
相关标签/搜索