一、ArrayList 类

这里分析jdk 1.8,包含一些1.8新特性的源码
继承树如下:

20170415022359404.jpg

宏观上说,ArrayList是基于动态数组实现的,数组具有按索引查找的特性,所以访问很快,适合经常查询的数据。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

二、ArrayList属性

    //序列号
    private static final long serialVersionUID = 8683452581122892189L;
    //默认容量
    private static final int DEFAULT_CAPACITY = 10;
    //一个空数组,当用户指定该 ArrayList 容量为 0 时,返回该空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /*一个空数组实例
    * - 当用户没有指定 ArrayList 的容量时(即调用无参构造函数),返回的是该数组==>刚创建一个 ArrayList 时,其内数据量为 0。
    * - 当用户第一次添加元素时,该数组将会扩容,变成默认容量为 10(DEFAULT_CAPACITY) 的一个数组===>通过  ensureCapacityInternal() 实现
    * 它与 EMPTY_ELEMENTDATA 的区别就是:该数组是默认返回的,而后者是在用户指定容量为 0 时返回*/
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
     //ArrayList基于数组实现,用该数组保存数据, ArrayList 的容量就是该数组的长度
     //该值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入 ArrayList 中时,数组将扩容值 
    private transient Object[] elementData; //transient:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。
    //ArrayList实际存储的数据数量
    private int size;

三、ArrayList构造方法

//创建一个初试容量的、空的ArrayList,当初试容量值非法(小于0)时抛出异常
public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
 
//无参构造函数:创建一个 空的 ArrayList,此时其内数组缓冲区 elementData = {}, 长度为 0,当元素第一次被加入时,扩容至默认容量 10
public ArrayList() {
        super();
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 
//创建一个包含collection的ArrayList,要放入 ArrayList 中的集合其内元素将会全部添加到新建的 ArrayList 实例中
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray(); //将集合转化成Object[]数组
        size = elementData.length;   //把转化后的Object[]数组长度赋值给当前ArrayList的size,并判断是否为0
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 这句话意思是:c.toArray 可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug
        if (elementData.getClass() != Object[].class) { // 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf(); 来构造一个大小为 size 的 Object[] 数组
            elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // 替换空数组
            this.elementData = EMPTY_ELEMENTDATA;
        }
}

四、ArrayList常用方法

public void trimToSize()

将数组缓冲区大小调整到实际 ArrayList 存储元素的大小

    public void trimToSize() {
        // modCount 是 AbstractList 的属性值:protected transient int modCount = 0;
        // [问] modCount 有什么用?
        modCount++;
        // 当实际大小 < 数组缓冲区大小时
        // 如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1
        // 通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况
        if (size < elementData.length) {
            // 注意这里:这里的执行顺序不是 (elementData = (size == 0) ) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
            // 而是:elementData = ((size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size));
            // 这里是运算符优先级的语法
            // 调整数组缓冲区 elementData,变为实际存储大小 Arrays.copyOf(elementData, size)
            //先判断size是否为0,如果为0:实际存储为EMPTY_ELEMENTDATA,如果有数据就是Arrays.copyOf(elementData, size)
            elementData = (size == 0)
                    ? EMPTY_ELEMENTDATA
                    : Arrays.copyOf(elementData, size);
        }
    }

public void ensureCapacity(int minCapacity)

指定 ArrayList 的容量

public void ensureCapacity(int minCapacity) {
        // 最小扩充容量,默认是 10
        //这句就是:判断是不是空的ArrayList,如果是的最小扩充容量10,否则最小扩充量为0
        //上面无参构造函数创建后,当元素第一次被加入时,扩容至默认容量 10,就是靠这句代码
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                ? 0
                : DEFAULT_CAPACITY;
        // 若用户指定的最小容量 > 最小扩充容量,则以用户指定的为准,否则还是 10
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

 private void ensureCapacityInternal(int minCapacity):明确 ArrayList 的容量,提供给本类使用的方法
 private void ensureExplicitCapacity(int minCapacity):明确 ArrayList 的容量,用于内部优化,保证空间资源不被浪费:尤其在 add() 方法添加时起效
 private void grow(int minCapacity):扩容,以确保 ArrayList 至少能存储 minCapacity 个元素
 private static int hugeCapacity(int minCapacity):大容量分配,最大分配 Integer.MAX_VALUE

返回ArrayList实际存储的元素数量

public int size() {
        return size;
    }

判断ArrayList是否有元素

public boolean isEmpty() {
        return size == 0;
    }

是否包含o元素

public boolean contains(Object o) {
        // 根据 indexOf() 的值(索引值)来判断,大于等于 0 就包含
        // 注意:等于 0 的情况不能漏,因为索引号是从 0 开始计数的
        return indexOf(o) >= 0;
    }

public int indexOf(Object o)

顺序查找,返回元素的最低索引值(最首先出现的索引位置)

public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

public int lastIndexOf(Object o)

逆序查找,返回元素的最低索引值(最首先出现的索引位置)

public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

public Object clone()

实现的有Cloneable接口,深度复制:对拷贝出来的 ArrayList 对象的操作,不会影响原来的 ArrayList

public Object clone() {
        try {
            // Object 的克隆方法:会复制本对象及其内所有基本类型成员和 String 类型成员,但不会复制对象成员、引用对象
            ArrayList<?> v = (ArrayList<?>) super.clone();
            // 对需要进行复制的引用变量,进行独立的拷贝:将存储的元素移入新的 ArrayList 中
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

返回 ArrayList 的 Object 数组

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

public E get(int index)

获取指定位置上的元素,从0开始

    public E get(int index) {
        rangeCheck(index);//检查是否越界
        return elementData(index);
    }
    // 检查数组是否在界线内
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

public E set(int index, E element)

设置 index 位置元素的值

public E set(int index, E element) {
        rangeCheck(index);//越界检查
 
        E oldValue = elementData(index);//获取旧数值
        elementData[index] = element;
        return oldValue;
    }

public boolean add

增加指定的元素到ArrayList的指定位置,默认最后位置

public boolean add(E e) {
        // 确定ArrayList的容量大小---严谨
        // 注意:size + 1,保证资源空间不被浪费,
        // ☆☆☆按当前情况,保证要存多少个元素,就只分配多少空间资源
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
 
//在这个ArrayList中的指定位置插入指定的元素
public void add(int index, E element) {
        rangeCheckForAdd(index);//判断角标是否越界
        //看上面的,size+1,保证资源空间不浪费,按当前情况,保证要存多少元素,就只分配多少空间资源
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //第一个是要复制的数组,第二个是从要复制的数组的第几个开始,
        // 第三个是复制到那,四个是复制到的数组第几个开始,最后一个是复制长度
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

public E remove

移除指定位置的元素 或者指定元素

public E remove(int index) {
        rangeCheck(index);
 
        modCount++;
        E oldValue = elementData(index);
 
        int numMoved = size - index - 1;//要移动的长度
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        // 将最后一个元素置空
        elementData[--size] = null;
 
        return oldValue;
    }
 
//移除list中指定的第一个元素(符合条件索引最低的)
 public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

更多方法请移步ArrayList源码分析

Last modification:December 25th, 2019 at 11:57 am
如果觉得我的文章对你有用,请随意赞赏
评论打卡也可以哦,您的鼓励是我最大的动力!