ArrayList源码解析

1.类图

文章使用的源码是JDK 1.8.0_181版本

ArrayList源码解析_第1张图片

 

分析主要从下面几个点入手:

  1. 体系结构
  2. 构造方法
  3. 和LinkedList,Vector的区别
  4. 扩容

2.ArrayList的体系结构

ArrayList直接实现了下面这几个接口:

  1. List         有序集合接口,定义了一些基础方法,自实现了sort方法和replaceAll方法
  1. RandomAccess  标记接口,实现这个接口可以实现快速随机访问
  2. Cloneable       用来重写clone方法
  3. java.io.Serializable  序列化接口
  4. 继承AbstractList

 

3.构造方法

字段属性

ArrayList提供了三个构造方法,在解析这几个构造方法之前,需要先说一下ArrayList中的几个重要的属性。

	/**
     * Default initial capacity.
	   默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
		用于空实例的共享空数组实例。
     */
    private static final Object[] 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.
	 
	 用于默认大小的空实例的共享空数组实例。 我们将此与EMPTY_ELEMENTDATA区分开来,以便了解在添加第一个元素时要膨胀多少。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_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.
	 存储ArrayList元素的数组缓冲区.ArrayList的容量是此数组缓冲区的长度。添加第一个元素时,任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都将扩展为DEFAULT_CAPACITY。
	 这个数组是ArrayList对象实际保存数据的地方
     */
    transient Object[] elementData; // 非私有,以简化嵌套类访问

    /**
     * The size of the ArrayList (the number of elements it contains).
     *	ArrayList的大小(它包含的元素数)。
     * @serial
     */
    private int size;

 

无参构造

ArrayList源码解析_第2张图片

这个构造方法对应的实例化方法是下面这种写法,也是最常见的写法。

方法逻辑

无参构造里对elementData数组进行实例化,将elementData数组指向常量空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,长度为0,当第一次添加数据时,数组的长度会变成10。

 

对象创建时的elementData数组的长度

ArrayList源码解析_第3张图片

添加一个元素之后elementData数组的长度

ArrayList源码解析_第4张图片

指定初始容量的构造方法

ArrayList源码解析_第5张图片

对应下面这种写法:

方法逻辑

指定长度如果大于0,将elementData数组实例化为指定长度的数组。

指定长度如果等于0,将elementData指向长度为0的空数组常量。

指定长度如果小于0,抛出 IllegalArgumentException 异常

 

指定长度初始化之后elementData数组的长度:

ArrayList源码解析_第6张图片

添加元素之后elementData数组的长度:

ArrayList源码解析_第7张图片

可以看到指定长度初始化的ArrayList对象在添加元素之后elementData数组长度也不会变化。

使用Collection对象构造

ArrayList源码解析_第8张图片

这种写法比较多,这里给出一个简单的例子

 

方法逻辑

传入一个实现了Collection接口的对象(下文简称为c),然后将这个对象转成数组,并且将elementData数组指向这个这个数组。然后判断elementData数组的长度是否等于0,如果不等于0,并且c.toArray()返回的不是Object[]对象,则把c的内容copy到elementData中。如果长度等于0,则将elementData指向一个空数组。

 

    1. 和LinkedList,Vector的区别

这是一个经典的面试题和笔试题。先说一下这三个集合类的相同点。

相同点:

  1. 都实现了Collection,List,Serializable接口
  2. ArrayList和Vector都是使用数组存储数据
  3. 都是有序集合
  4. ArrayList和LinkedList都是线程不安全的

 

不同点:

  1. ArrayList是基于动态数组的数据结构,LinkedList是基于双向链表数据结构
  2. Vector是线程安全的
  3. 随机get和set时ArrayList 性能要优于LinkedList
  4. add和remove时LinkedList 性能要优于ArrayList

 

5.扩容

add方法的源码

ArrayList源码解析_第9张图片

可以看到在add方法之前调用了ensureCapacityInternal方法,传入的参数是size+1,size就是当前集合中的参数个数,可以确定这个方法就是扩容的方法了

ArrayList源码解析_第10张图片

 

calculateCapacity 方法中先判断了elementData数组是否为空,如果为空判断则返回minCapacity 和10中最大的数值,如果不为空,则直接返回minCapacity。然后进入到ensureExplicitCapacity方法,对modCount做++操作,这个参数用于集合的Fail-Fast机制判断中。方法里判断新增元素之后大小是否超过当前集合元素个数,如果超过,则调用grow方法。

ArrayList源码解析_第11张图片

这个方法就是真正用于集合扩容的方法了,可以看到集合新容量的计算公式为:

新容量 = 现有容量 + 现有容量 / 2

如果新容量小于minCapacity ,则新长度等于minCapacity,如果新容量大于最大长度(2147483639),调用hugeCapacity 方法,传入参数为minCapacity,也就是数组扩容后需要最小容量。如果minCapacity大于MAX_ARRAY_SIZE,则返回Integer的最大值。否则返回MAX_ARRAY_SIZE

ArrayList源码解析_第12张图片

ArrayList源码解析_第13张图片

ArrayList源码解析_第14张图片

 

调用完grow方法之后将elementData中的元素复制到一个新的大容量数组中。Arrays.copy方法是  System.arraycopy 方法。因为方法是native修饰的,所以无法查看源码。

 

你可能感兴趣的:(JDK源码解析,集合源码,集合源码)