【Java框架集合】之ArrayList源码解析

ArrayList源码解析

(注意:本博客与其他框架集合的文章解析的均使用 JDK1.8 的源码)

一、继承与实现

在这里插入图片描述

(1)继承了如下类:
ArrayList<E> extends 
	AbstractList<E>   抽象类   这个类提供了List接口的框架实现,
					           以最小化实现这个“随机访问”数据存储(如数组)支持的接口所需的工作量。
(2)实现了如下接口:
ArrayList  implements
	List<E>  	  List接口     该接口可以精确控制列表中每个元素的插入位置,
					   	用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。 
					     
	RandomAccess  随机存取接口  此接口的主要目的是允许通用算法更改其行为,
							       以便在应用于随机访问列表或顺序访问列表时提供良好的性能。
	Cloneable     克隆接口     不实现Cloneable接口的实例上调用对象的克隆方法导致抛出异常CloneNotSupportedException。
	
	Serializable  序列化接口    不实现此接口的类将不会在任何状态序列化或反序列化。可序列化类的所有子类型都是可序列化的。

二、变量

ArrayList的所有变量
在这里插入图片描述

(1) 以 private static final 开头
		 long    serialVersionUID = 8683452581122892189L;   序列化标识
		 
		  int    DEFAULT_CAPACITY = 10;    默认初始容量为10
		  
		Object[] EMPTY_ELEMENTDATA = {};   用于空实例的共享空数组实例
		
		Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  用于默认大小的空实例的共享空数组实例。
		我们将其与EMPTY_ELEMENTDATA区分开来,以了解何时需要膨胀多少添加第一个元素
						
		   int    MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;  //最大数组的大小为 Integer.MAX_VALUE - 8
(2)其他
	transient Object[] elementData;
			存储ArrayList元素的数组缓冲区。ArrayList的容量就是这个数组缓冲区的长度。
			当添加第一个元素时,任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都将扩展到DEFAULT_CAPACITY。
	
	private int size;    ArrayList的大小(它包含的元素的数量,也就是说数据有多少个,不是指长度!)

三、构造函数

(1) 无参构造

在这里插入图片描述

(2) 有参构造(给定初始容量)

在这里插入图片描述

(3)有参构造 (将另一个集合的数据添加到当前集合里)

在这里插入图片描述

四、方法

(分别讲解增删改查,以及常用的方法)

1、“增” 方法(添加方法)

(下图是JDK1.8 API帮助文档的集合(ArrayList)添加方法(add)图)
在这里插入图片描述
从上图中,可以看出有add和addAll方法,这两个方法还重写了一遍
(注意:变量 modCount 是来自 AbstractList 抽象类,表示 修改内部机构的次数)

(1)public boolean add (E e) { … }

在这里插入图片描述

方法作用: 将指定的元素追加到此列表的末尾。
方法过程:
  1. 调用 ensureCapacityInternal方法 进行 计算内部容量和 数组扩容 (同时增加modcount次数)
    (1)调用 calculateCapacity方法 进行 计算数组的容量
    (2)调用 ensureExplicitCapacity方法 进行 将modcount自增修改内部结构次数, 在根据计算好的容量,决定是否对这个数组进行扩容,如果要进行扩容则进入 grow方法
  2. 将元素数组添加到数组的末尾
  3. 返回add结果(true 成功)
(2)public boolean add (int index, E element) { … }

在这里插入图片描述
在这里插入图片描述

方法作用: 在此数组中的指定位置插入指定的元素。
方法过程:
  1. 调用 rangeCheckForAdd方法 检查用户输入的index是否合法
  2. 调用 ensureCapacityInternal方法 进行 计算内部容量和 数组扩容 (同时增加modcount次数)
    (1)调用 calculateCapacity方法 进行 计算数组的容量
    (2)调用 ensureExplicitCapacity方法 进行 将modcount自增修改内部结构次数, 在根据计算好的容量,决定是否对这个数组进行扩容,如果要进行扩容则进入 grow方法
  3. 调用 System系统的arraycopy方法 将被插入位置的元素以及后面的元素 全部向后移动
  4. 将元素插入到指定的索引位置
  5. size自增(实际数据数量 + 1)
(3)public boolean addAll (Collection<? extends E> c) { …… }

在这里插入图片描述

方法作用: 将指定集合中的所有元素按指定集合的迭代器返回的顺序追加到此列表的末尾。
方法过程:
  1. 先将传入的集合转为数组
  2. 获取该数组的长度
  3. 调用方法 ensureCapacityInternal方法 进行内部扩容 同时增加修改内部结构次数
  4. 传入的集合转为数组的数组 填充到ArrayList数组null值部分
  5. 将size(数组实际数据的数量)添加传入的集合转为数组的数组的长度
  6. 如果 数组的长度 为0 则返回 false 反之返回 true
(4)public boolean addAll (int index, Collection<? extends E> c) { …… }

在这里插入图片描述

方法作用: 将指定集合中的所有元素插入此列表,从指定位置开始。
方法过程:
  1. 调用 rangeCheckForAdd方法 检查用户输入的index是否合法
  2. 将传入的集合转为数组
  3. 获取该数组的长度
  4. 调用 ensureCapacityInternal方法 进行 计算内部容量和 数组扩容 (同时增加modcount次数)
    (1)调用 calculateCapacity方法 进行 计算数组的容量
    (2)调用 ensureExplicitCapacity方法 进行 将modcount自增修改内部结构次数, 在根据计算好的容量,决定是否对这个数组进行扩容,如果要进行扩容则进入 grow方法
  5. 获取数组需要移多少位元素(实际数据的数量 - 插入位置 = 数组移动的元素)
  6. 检查(数组移动的元素)是否大于0
    (1)如果大于零(表示需要数组的被插入的位置后面还有元素需要移动)
    调用 System系统的arraycopy方法 将被插入位置的元素以及后面的元素 全部向后移动
  7. 调用 System系统的arraycopy方法 将集合的数组插入到索引指定的位置
  8. size添加集合数组的长度
  9. 如果 集合数组的长度 为0 则返回 false 反之返回 true
(5)计算容量 和 扩容

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法作用: 计算数组容量 和 扩容数组容量
方法过程:
  1. 调用 calculateCapacity方法 进行 计算数组的容量
    (1)判断 elementData 是否为 默认空实例数组
    》》》结果为 true:则调用Math的max方法比较 默认初始容量实际最小容量 哪个比较大就返回哪个
    》》》结果为 false:则直接返回 实际最小容量
  2. 调用 ensureExplicitCapacity方法 进行 将modcount自增修改内部结构次数, 在根据计算好的容量,决定是否对这个数组进行扩容,如果要进行扩容则进入 grow方法
    (1)先将 modcount 自增(增加内部结构修改次数)
    (2)判断 实际最小容量 减去(-)数组长度 > 0 结果 为true时进行扩容
    》》》结果为 false:则取消对数组的扩容处理(表示在这次添加元素中,数组的容量可以支撑)
    》》》结果为 true:grow方法 对数组进行扩容处理
    》》》》》》1、获取数组长度(int oldCapacity = elementData.length;
    》》》》》》2、获取新的数组容量 (int newCapacity = oldCapacity + (oldCapacity >> 1);
    》》》》》》3、检查 新的数组容量 - 所需的最小容量 是否 小于 0
    》》》》》》》》》结果为 true:则将所需的最小容量赋值给新的数组容量
    》》》》》》》》》结果为 false:不做任何操作(跳过此步骤)
    》》》》》》4、检查 新的数组容量 - 数组最大大小 是否 大于 0
    》》》》》》》》》结果为 false:不做任何操作(跳过此步骤)
    》》》》》》》》》结果为 true:则调用 hugeCapacity方法 在方法结束后,赋值给新的数组容量
    》》》》》》》》》》》》》》》(1)检查 实际最小容量 是否小于 0
    》》》》》》》》》》》》》》》》》》如果为true:抛出内存溢出异常
    》》》》》》》》》》》》》》》》》》如果为false:则跳过此步骤
    》》》》》》》》》》》》》》》(2)比较 所需的最小容量 > 数组最大大小
    》》》》》》》》》》》》》》》》》》如果为true:返回 Integer的最大值(Integer.MAX_VALUE)
    》》》》》》》》》》》》》》》》》》如果为false:返回 数组最大的值(MAX_ARRAY_SIZE)
    》》》》》》5、复制指定的数组,将数组扩容后,返回一个数组赋值给 elementData数组

2、“删” 方法(删除方法)

(下图是JDK1.8 API帮助文档的集合(ArrayList)删除方法(remove)部分图)
在这里插入图片描述

(1)public E remove(int index) { …… }

在这里插入图片描述
在这里插入图片描述

方法作用: 移除列表中指定位置的元素。
方法过程:
  1. 调用 rangeCheck方法 检查index下标是否正确
    》》》(1)判断 下标(index)是否大于 size(实际数据数量)
    》》》》》》如果是 true:(进入 outOfBoundsMsg方法 获取抛出异常的数据)抛出下标越界异常
    》》》》》》如果是 false:则跳过这个方法
  2. modcount 增加修改内部结构次数
  3. 进入 elementData方法 获取需要删除的元素数据
    》》》(1)直接return返回数组的元素(数组 [ 下标 ] )
  4. 获取需要移动多少位元素数据
  5. 判断(需要移动多少位数据)的值是否大于 0
    》》》如果 大于 0,则调用 System系统的arraycopy方法,将数据移动到指定的位置
    》》》如果 小于 0,则跳过该判断
  6. size自减后将数组中的(实际元素数据)的最后一位 赋值为 null(方便让GC回收空间内存)
  7. 返回被删除的元素数据
(2)public boolean remove(Object o) { …… }

在这里插入图片描述
在这里插入图片描述

方法作用: 如果指定元素出现,则从该列表中删除第一个出现的元素。
方法过程:
  1. 首先检查该值是否为空
    》》》(1)值 为 空(进入 if 代码块)
    》》》》》》1、 循环数据,封顶循环到size次数
    》》》》》》2、 每循环一次就判断一次该值是否为
    》》》》》》》》》a. 如果 值不为空:则 跳过判断 继续循环
    》》》》》》》》》b. 如果 值为空:则进入该判断调用 fastRemove方法 删除元素
    》》》》》》》》》》》》1、modCount 自增一次
    》》》》》》》》》》》》2、获取需要移动的元素数量
    》》》》》》》》》》》》3、当(需要移动的元素数量)大于零时,则进入 if 判断代码块
    》》》》》》》》》》》》》》》将数组 即将删除的元素 的后面的值,向左移动一步,覆盖删除的值即可
    》》》》》》》》》》》》4、size自减后将数组中的(实际元素数据)的最后一位赋值为null
    》》》》》》》》》b. 直接 return 返回 true (删除元素成功)
    》》》(2)值 不 为 空(进入 else 代码块)
    》》》》》》1、 循环数据,封顶循环到size次数
    》》》》》》2、 每循环一次就判断一次该值是否为 用户传入要删除的值
    》》》》》》》》》a. 如果 值不为空:则 跳过判断 继续循环
    》》》》》》》》》b. 如果 值为空:则进入该判断调用 fastRemove方法 删除元素
    》》》》》》》》》》》》1、modCount 自增一次
    》》》》》》》》》》》》2、获取需要移动的元素数量
    》》》》》》》》》》》》3、当(需要移动的元素数量)大于零时,则进入 if 判断代码块
    》》》》》》》》》》》》》》》将数组 即将删除的元素 的后面的值,向左移动一步,覆盖删除的值即可
    》》》》》》》》》》》》4、size自减后将数组中的(实际元素数据)的最后一位赋值为null
    》》》》》》》》》b. 直接 return 返回 true (删除元素成功)
(3)public boolean removeAll(Collection<?> c) { …… }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法作用: 从该列表中删除指定集合中包含的所有元素。
方法过程:
  1. 首先检查 传入的集合是否为空(调用Objects的requireNonNull方法 检查)
  2. 直接 return 返回调用 batchRemove方法 的返回结果
    》》》(1)创建一个Object数组将ArrayList数组本身赋值给它
    》》》(2)定义两个变量 r 和 w,并指定初始值为 0
    》》》(3)定义boolean类型的变量 modified 并指定初始值为 false
    》》》(4)try 代码块
    》》》》》》 循环数组,每循环一次,就判断 (根据本方法内的elementData数组和下标r 来获取数据)该数据是否在 传入的集合当中,当检查是否存在该集合后 紧接着判断是否和传入的 complement变量 是否一致(false)也就是当结果为如下:
    》》》》》》》》》存在该集合 true == false:代表该元素是需要被删除的数据,所以不需要对该元素进行处理
    》》》》》》》》》不存在该集合 false == false:代表该元素是不需要被删除的数据,所以将该元素赋值给(本方法内的elementData数组)从0开始,也就是对应的变量w
    》》》(5)finally 代码块
    》》》》》》判断(在 try代码块 是否出现异常错误显现)r 不等于 size, 则代表出现异常错误等问题
    》》》》》》》》》a. 调用系统System的arraycopy方法,从变量r开始和后面的值赋值到,w开始及后面的位置,赋值size - r次
    》》》》》》》》》b. 当 try{ }里的for循环 出现异常错误时,size - r就是表示实际的数据数量
    》》》》》》判断(在删除集合元素后的(实际数据的数量))w 不等于 size,则表示数组中在下标w及后面的位置不是有效的值
    》》》》》》》》》a. 循环将从 下标w开始及后面的位置 全部赋值为空
    》》》》》》》》》b. modcount 添加 size - w 的次数 (size - w 表示的意思是修改的次数)
    》》》》》》》》》c. 此时的 “w” 才是正真意义上的 实际数据的数量,所以将 w 赋值给 size
    》》》》》》》》》d. 将变量modified 赋值为true 表示删除成功
    》》》(6)return返回 modified变量(表示是否删除成功)
(4)protected void removeRange(int fromIndex, int toIndex) { …… }在这里插入图片描述
方法作用:从该列表中删除其索引位于 fromIndex(包含) 和 toIndex(排除) 之间的所有元素
方法过程:
  1. 首先将modcount自增(增加内部几个修改次数)
  2. 获取移动多少位元素数据
  3. 将指定源数组中的数组从指定位置复制到目标数组的指定位置
  4. 统计实际数据的数量
  5. 将(实际数据的数量)后面的元素位置赋值为null
  6. 将 统计好的实际数据的数量(newSize)赋值给size

3、“改” 方法(修改方法)

(下图是JDK1.8 API帮助文档的集合(ArrayList)修改方法(set)图)
在这里插入图片描述

(1)public E set (int index, E element) { …… }在这里插入图片描述
方法作用: 将列表中指定位置的元素替换为指定元素。
方法过程:
  1. 首先调用 rangeCheck方法 检查 index下标是否合法
  2. 调用 elementData方法 根据下标直接返回元素数据 赋值给 oldValue
  3. 将替换的元素数据 赋值到指定的索引位置
  4. 返回被替换的元素数据

4、“查” 方法(查询方法)

(下图是JDK1.8 API帮助文档的集合(ArrayList)查询方法(get、contains)图)
在这里插入图片描述

(1)public E get(int index) { …… }在这里插入图片描述
方法作用: 返回列表中指定位置的元素。
方法过程:
  1. 调用 rangeCheck方法 检查下标(index)是否合法
  2. 直接 return 调用 elementData方法 的返回数据

5、常用方法

(1)trimToSize 方法

在这里插入图片描述

方法作用: 修改这个 ArrayList实例的容量是列表的当前大小。
方法过程:
  1. 首先将 modCound自增
  2. if 判断 数组的长度 是否大于 size(数组实际数据的数量)
    》》》不大于:则跳过 if 判断
    》》》大于:则检查(实际数据的数量)是否等于零
    》》》》》》(1)等于 零:将 EMPTY_ELEMENTDATA 空实例数组 赋值给 elementData数组
    》》》》》》(2)不等于 零:调用Arrays的copyof方法获取数组的全部有值数据,赋值给 elementData数组
(2)contains 方法在这里插入图片描述

在这里插入图片描述

方法作用:检查元素是否存在次列表中
方法过程:
  1. 直接 return 调用 indexOf方法 结束方法后,判断该方法的返回值是否 大于等于 零
    》》》(1)当值 等于 null:循环数组 判断数组里是否有null的值,如果有,就return返回该null值的下标
    》》》(2)当值 不等于 null:循环数组 判断数组里是否有对应的值,如果有,就return返回该对应值的下标
    》》》(3)当值不存在该数组时,return返回-1(表示不存在或者未找到)
(3)clone 方法在这里插入图片描述
方法作用:返回此 ArrayList实例的浅拷贝。
方法过程:
  1. 调用 父类的克隆方法,强转为 ArrayList<?>类型
  2. 将本类(ArrayList)的数组(elementData) 按照size大小,依次赋值给 新的ArrayList数组
  3. 将新的ArrayList数组的modCount(修改内部结构次数)赋值为空
  4. 将新的ArrayList返回
(4)indexOf 方法在这里插入图片描述
方法作用: 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
方法过程:
  1. 检查该值是否为空
    》》》值 == null:循环数组 判断数组里是否有null的值,如果有,就return返回该null值的下标
    》》》值 != null:循环数组 判断数组里是否有对应的值,如果有,就return返回该对应值的下标
  2. 当值不存在该数组时,return返回-1(表示不存在或者未找到)
(5)isEmpty 方法在这里插入图片描述
方法作用:如果此列表不包含元素,则返回 true 。
方法过程:
  1. 直接返回 size
(6)lastIndexOf 方法在这里插入图片描述
方法作用: 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
方法过程:
  1. 检查该值是否为空
    》》》值 == null:从后往前循环数组 判断数组里是否有null的值,如果有,就return返回该null值的下标
    》》》值 != null:从后往前循环数组 判断数组里是否有对应的值,如果有,就return返回该对应值的下标
  2. 当值不存在该数组时,return返回-1(表示不存在或者未找到)
(7)size 方法在这里插入图片描述
方法作用: 返回列表中的元素数量。
方法过程:
  1. 直接return返回 size(实际数据的数量)

6、结语

以上则是ArrayList必要重要的源码解析,本博客还有其他源码解析,如果还没更新,请静待更新
(如有看到有错误或者有疑问的地方,请在下方评论留言,谢谢)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值