ArrayList源码简单分析

ArrayList源码简单分析

一. ArrayList简介

ArrayList是Java中使用最频繁的集合之一,实现了List接口。它的底层基于Array数组实现容器容量自动变化,可以存入任何值,包括null。ArrayList还是实现了RandomAccess, Cloneable, Serializable接口,所以它还支持随机访问/克隆和序列化。

二. 成员属性

2.1 DEFAULT_CAPACITY

/**
  * Default initial capacity.
  */
private static final int DEFAULT_CAPACITY = 10;
  • 默认集合容量初始大小
  • 第一次向集合(无参构造)中放入数据时,会根据DEFAULT_CAPACITY对集合的容量进行初始化

2.2 EMPTY_ELEMENTDATA

/**
 * Shared empty array instance used for empty instances.
 */
private static final Object[] EMPTY_ELEMENTDATA = {};
  • 空数组对象
  • 出现在有参构造方法中

2.3 DEFAULTCAPACITY_EMPTY_ELEMENTDATA

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  • 空数组对象
  • 出现在无参构造方法中
  • 该属性和EMPTY_ELEMENTDATA进行区分,当第一次添加元素时集合该如何进行扩容。

2.4 elementData

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 */
transient Object[] elementData;
  • 数组对象
  • 添加进来的元素会被储存在该buffer数组中
  • ArrayList集合的容量是该数组的长度
  • 当elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,第一次添加元素,集合的容量将会被扩展为DEFAULT_CAPACITY = 10。

2.5 size

/**
 * The size of the ArrayList (the number of elements it contains).
 * @serial
 */
private int size;
  • 集合实际包含元素的数量

2.6 注意

虽然DEFAULTCAPACITY_EMPTY_ELEMENTDATA和EMPTY_ELEMENTDATA的值相等都是空对象{},但它们的作用相当于标识符。

如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,那么就意味着ArrayList集合是通过无参构造函数创建的。

相反,elementData == EMPTY_ELEMENTDATA,ArrayList集合是通过两个有参构造函数中的任意一个创建的。

这些都会在主要操作方法中发挥作用,之后会看到

三. 构造函数

3.1 无参构造

在这里插入图片描述

  • 使用默认的容量大小来构造一个空的列表
  • 具体的生成会在第一次添加元素时体现(调用add方法)

3.2 有参构造

3.2.1 initialCapacity

在这里插入图片描述

  • 通过指定的容量大小来构造空列表(数组对象)
  • initialCapacity > 0,直接new一个Object类型的数组
  • initialCapacity == 0,将默认的空对象赋值给elementData
3.2.2 Collection

在这里插入图片描述

  • 将集合转换为数组,elementData引用指向数组
  • 数组elementData的长度赋值给size,判断size是否不等于零
  • size == 0,数组elementData的值等于空数组
  • size != 0,先判断elementData数组的类型是否为Object (c.toArray可能出错,并没有返回Object[])
  • 如果数组elementData的类型不是object,那么就复制一个全新的类型为Object的数组并赋值给elementData

四. 主要操作方法

4.1 add

在这里插入图片描述

  • 将元素添加到集合
  • minCapacity最小容量,插入第一个元素,所以集合的容量最小为1
  • 调用ensureCapacityInternal方法(作用:每次调用add方法时都会检查集合的容量是否够用)

在这里插入图片描述

  • 在ensureCapacityInternal方法中
    • 直接调用了ensureExplicitCapacity方法,参数由calculateCapacity方法返回值提供。
    • calculateCapacity方法,判断集合的构建是无参构造还是有参构造。如果是无参构造,返回DEFAULT_CAPACITY,所以集合的初始容量为10;如果是有参构造,返回minCapacity,集合的初始容量是根据有参构造传递的参数决定的。
  • ensureExplicitCapacity方法
    • modCount操作数+1
    • minCapacity - elementData.length > 0,意味着此时的集合的容量已经没办法添加元素了,所以接下来要调用grow方法来给集合扩容。
  • 调用grow方法

在这里插入图片描述

  • oldCapacity + (oldCapacity >> 1) 相当于oldCapacity * 1.5,将集合原来的容量扩大到原来的1.5倍
  • 扩容之后也不一定会适合,有可能太大了也有可能太小了,所以就有了最后两个if语句。
  • 如果扩容之后太小了,那么就直接将我们需要的最小容量赋值给newCapacity
  • 如果扩容之后太大了,那么就将MAX_ARRAY_SIZE或者是Integer.MAX_VALUE赋值给newCapacity
  • 然后将原数组中的数据复制到大小为newCapacity的新数组中,并将新数组赋值给elementData。

4.1.1 其它的一些add方法

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

  • 以上三种方法和前面讲的add方法都差不多。
  • 在插入数据之前都要先判断当前集合的容量是否足够,如果不够就扩容。
  • 之后就是通过System.arraycopy方法将插入的数据复制到elementData数组中。

4.1.2 System.arraycopy

public static native void arraycopy(Object src,  int  srcPos,  Object dest, int destPos,int length)
  • 从指定的源数组复制数组,其实就是将一个指定的数组中的数据复制到另一个数组中去
  • 参数
    • src 源数组,即被指定的数组
    • srcPos 开始的位置,即被指定数组的数据从那个位置开始被复制
    • dest 目标数组,即被复制的数据最后被存放的数组
    • destPos 开始的位置,即被复制的数据最后被存放到数组具体位置
    • length 被复制的数组的元素长度。

4.2 remove

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

  • 当我们调用remove(int index)方法时,首先要先检查下标是否越界,然后操作数加一,取出将要被溢出的值。numMoved 为要移动的元素的位数,如果numMoved==0,则说明被移除的元素是最后一位,该元素后面的所有元素不需要移动,直接将最后一位置为null,后面的事就交给GC来处理。如果numMoved>0,那么就将被移除元素后面所有的元素向前移动numMoved个位置,然后再将最后numMoved个位置的值设为null。
  • 当我们调用remove(Object o)方法时,会根据被移除的对象是否为null进行不同的处理,其实都差不多。先根据被删除对象找到该对象的位置,之后就跟remove(int index)方法一样了。

4.3 get

在这里插入图片描述

  • get(int index)方法最简单,当代用get方法时,先检查index下标是否越界,如果越界就抛异常。否则,就直接根据index下标从数组elementData中,取出该位置的值,并返回。

五. 感谢

  • 本篇笔记是我看了微信公众号好好学java中的文章写出来的,内容的当然没有大佬写的那么详细和有条理,所以大家如果有兴趣可以去订阅一下。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值