ArrayList-源码解读

简介:

ArrayListList 接口的一个实现类,它是程序中最常见的一种集合。

在这里插入图片描述

在 ArrayList 内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList 会在内存中分配一个更大的数组来存储这些元素,因此可以将 ArrayList 集合看作一个长度可变的数组。

正是由于 ArrayList 内部的数据存储结构是数组形式,在增加或删除指定位置的元素时,会创建新的数组,效率比较低,因此不适合做大量的增删操作。但是,这种数组结构允许程序通过索引的方式来访问元素,因此使用 ArrayList 集合在遍历和查找元素时显得非常高效。

以下我会通过一个简单的 Demo 去阅读 ArrayList 的源码,分析它为什么可以自动扩容


源码解析

结论(先给出结论便于理解):

  • 1)ArrayList 中维护了一个 Object 类型的数组 elementData
  • 2)当创建 ArrayList 对象时,如果使用的是无参构造器,则初始 elementData 容器为 0,第一次添加,则扩容 elementData 为 10,如需再次扩容,则扩容 elementData 为 `1.5 倍
  • 3)如果使用的是指定大小的构造器,则初始 elementData 容器为指定大小,如果需要扩容,则直接扩容 elementData 为 1.5 倍

PS:以下源码来源于 JDK1.8

这里我写了一个测试方法,通过 debug 的方式去阅读源码,打上断点,运行

ArrayList-源码解读_第1张图片

如果你是使用 IDEA 通过 Debug去阅读源码的话,建议把箭头所指的勾选给去掉,这样看到的信息会比较全一点。

ArrayList-源码解读_第2张图片

先执行以下这行代码

        // 使用无参构造创建 ArrayList 对象
        ArrayList list = new ArrayList();

无参构造方法源码:

程序会走到 ArrayList 类的无参构造方法中,执行 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 这样一段代码

ArrayList-源码解读_第3张图片

跟进 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,它默认是一个空的 Object[] 数组对象。

在这里插入图片描述

所以在使用无参构造创建 ArrayList 对象时,ArrayList 对象中的 elementData 属性初始化的时候就是一个 Object 类型的空数组


接着执行以下代码往集合中添加元素

        // 添加元素
        list.add(1);

add() 方法源码:

进入 ArrayList 类的 add 方法中

ArrayList-源码解读_第4张图片

该方法中做了两件事:

  • ① 确定当前集合是否需要扩容;
  • ② 对数组的下一个坐标进行赋值

这里最主要的就是 ensureCapacityInternal() 这个方法,进入该方法

ArrayList-源码解读_第5张图片

  • ① 会先判断 elementData 是否为空集合 {}

    • 如果为空集合的话就需要确定最小容量 minCapacity,跟进 DEFAULT_CAPACITY 属性发现默认为 10
      在这里插入图片描述
      方法参数minCapacity 等于 size + 1,所以说如果没有指定集合长度,在第一次添加元素的时候,集合初始化大小为 10
  • ② 再进入到 ensureExplicitCapacity() 该方法中

ArrayList-源码解读_第6张图片

该方法

  • modCount++,modCount 是用于记录当前集合被修改的次数,它的意义就是为了防止多个线程同时去修改而出现异常
    ArrayList-源码解读_第7张图片
  • ② 判断最小容量是否大于当前集合的容量
    • 如果大于,则进入真正的扩容grow()方法
    • 如果小于,则表示不需要扩容

ArrayList-源码解读_第8张图片

以上代码就是底层扩容的核心了,大概逻辑如下:

  • ① 第一次扩容 newCapacity = 最小容量
  • ② 第二次及其以后,都是按照集合大小的 1.5 倍进行扩容
  • ③ 扩容使用的是 Arrays.copyOf() 方法

grow() 方法执行完以后,回到 add() 方法中,elementData 就有空间了,如果是使用无参构造创建的集合,并且第一次添加元素,那么 elementData = [null,null,null,null,null,null,null,null,null,null]


接着执行是有参构造的代码

		// 使用有参构造创建 ArrayList 对象,指定大小为 5
        ArrayList initList = new ArrayList(5);
        // 添加元素
        initList.add(1);

有参构造方法源码:

ArrayList-源码解读_第9张图片

这段代码的逻辑就是将 elementData 赋值为指定大小的 Object 数组对象,指定大小为负数的话就会抛出异常

之后又进入 add() 方法中

结论

  • 1)ArrayList 中维护了一个 Object 类型的数组 elementData
  • 2)当创建 ArrayList 对象时,如果使用的是无参构造器,则初始 elementData 容器为 0,第一次添加,则扩容 elementData 为 10,如需再次扩容,则扩容 elementData 为 `1.5 倍
  • 3)如果使用的是指定大小的构造器,则初始 elementData 容器为指定大小,如果需要扩容,则直接扩容 elementData 为 1.5 倍

你可能感兴趣的:(源码解读,java)