zoukankan      html  css  js  c++  java
  • ArrayList源码解析(一)

    正文

    源码解析系列主要对Java的源码进行详细的说明,由于水平有限,难免出现错误或描述不准确的地方,还请大家指出。

    1.位置

    ArrayList位于java.util包中。

    1 package java.util;
    2 
    3 import java.util.function.Consumer;
    4 import java.util.function.Predicate;
    5 import java.util.function.UnaryOperator;

    2.变量和常量

    先明确一点,ArrayList是采用Object类型的数组实现的。

    ArrayList开始定义了一些常量和变量:

    复制代码
    private static final long serialVersionUID = 8683452581122892189L;
    
        /**
         * Default initial capacity.
    * ArrayList初始化默认容量大小为10,见无参构造函数。 */ 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成员存储在这个数组缓冲区中。
    * 容量大小是这个缓冲区的长度,任何elementDate==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
    * 在添加第一个成员时都会扩充到DEFAULT_CAPACITY大小,即容量为10。
    */

    transient Object[] elementData; // non-private to simplify nested class access

    /**
    * The size of the ArrayList (the number of elements it contains).
    * ArrayList的大小,注意是实际包含的成员个数。
    * @serial
    */
    private int size;
    复制代码

    3.构造函数

    ArrayList有三个构造函数:

    1)无参构造函数

    复制代码
        /**
         * Constructs an empty list with an initial capacity of ten.
    * 构造一个初始容量为10的空list。 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
    复制代码

    注意,这里说构造一个初始容量为10的空list,但是赋给elementData的只是一个空的数组(原因后续说明)。

    2)带指定初始容量参数的构造函数

    复制代码
     /**
         * Constructs an empty list with the specified initial capacity.
         * 构造一个指定了初始容量的空list。
         * @param  initialCapacity  the initial capacity of the list
    * initialCapacity指定list的初始容量 * @throws IllegalArgumentException if the specified initial capacity * is negative
    * 当指定容量值为负值时,抛出IllegalArgumentException异常 */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
    复制代码

    当指定的初始容量值大于零时,新建一个大小为initialCapacity的Object数组,并赋 elementData;

    当指定的初始容量值为零时,将 EMPTY_ELEMENTDATA赋给elementData。

    3)使用指定Collection构造ArrayList的构造函数

    复制代码
        /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         * 构造一个包含指定Collection成员的list,成员的排列顺序与使用Collection的
    * iterator返回的顺序一致。 * @param c the collection whose elements are to be placed into this list
    * c 指定的Collection,它的所有元素都会被放在构造的list中 * @throws NullPointerException if the specified collection is null
    * 当指定的Collection为null是抛出NullPointerException异常 */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
    复制代码

    既然是从指定的Collection构造ArrayList,那么成员的类型就要保持不变,即ArrayList中的成员类型与Collection中的成员类型要相同。

    先利用toArray()将Collection c转换为数组,并让elementData指向这个数组;

    接下来,取出elementData的成员个数并赋值给size;

    如果size不为0,则判断elementData的成员类是否与Object类相同,不同则进行一次copy操作,并进行类型转换;

    如果size为0,则将elementData指向EMPY_ELEMENTDATA。

    4.trimToSize()方法

    复制代码
    /**
         * Trims the capacity of this <tt>ArrayList</tt> instance to be the
         * list's current size.  An application can use this operation to minimize
         * the storage of an <tt>ArrayList</tt> instance.
    * 将ArrayList实例的容量trim为list的当前size。
    * 应用可以使用这个方法来最小化ArrayList实例的存储空间。 */ public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
    复制代码

    modCount用来记录修改次数,modCount主要用于多线程环境下,因为ArrayList是非线程安全的,这里使用了Fail-Fast机制。任何对ArrayList的修改都会增加modCount的值,在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,会判断 modCount是否等于 expectedModCount,如果不等,说明其他线程修改了ArrayList,就要抛出ConcurrentModificationException 异常。

    理解这个函数的前提是分清楚size和length的不同之处:

    size是ArrayList实际包含的成员个数;而length是ArrayList的容量,即最多容纳多少元素。

    比如ArrayList的length可以为10,但是只包含6个成员,其余为null。

    trimToSize()方法的目的就是使length等于size,以节约存储空间。

    当size小于elementData的length时:

    如果size为0,说明 elementData 不包含任何成员,把elementData指向 EMPTY_ELEMENTDATA 即可;

    如果size不为0,说明elementData包含成员,这时调用copyOf(),做一次copy,使ArrayList的容量大小和size相等(这里主要目的是理解ArrayList,暂时不对copyOf()进行说明,后续文章详细介绍)。

    下篇文章我们主要分析ArrayList的扩容机制。

  • 相关阅读:
    执行.class文件
    Ant能干什么,编译?打包!
    C的随想
    微服务
    2018年宝鸡市高考复课报告会材料
    用图像解不等式
    高频易错题目01
    2018年宝鸡市二检数学题目解答
    点差法
    和事件的概率求法
  • 原文地址:https://www.cnblogs.com/zhangyuhang3/p/6910511.html
Copyright © 2011-2022 走看看