java ArrayList源码深入解析

一、定义

public class ArrayList  extends AbstractList implements RandomAccess, Cloneable, java.io.Serializable

 ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。

它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。

ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。稍后,我们会比较List的“快速随机访问”和“通过Iterator迭代器访问”的效率。
ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

二、属性

    //序列化id
    private static final long serialVersionUID = 8683452581122892189L;

    //默认数组初始的容量
    private static final int DEFAULT_CAPACITY = 10;

    //空的对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //默认的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //实际上真正保存数据的数组,从此出可以看出ArrayList使用Object数组来保存数据,transient表示不可序列化
    transient Object[] elementData;

    //数组实际包含元素的个数
    private int size;

三、构造方法

ArrayList提供了三种方式的构造器。

1.带有容量initialCapacity的构造方法

源码解释:

/**
     * 构造函数一
     * 如果大于0,会在ArrayList内部构建一个长度为initalCapacity的数组
     * 如果等于0,会将上述的静态EMPTY_ELEMENTDATA属性赋值给elementData,也不会产生新的数组。如果小于0,则抛出异常
     * @param initialCapacity 初始数组的容量
     */
    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);
        }
    }

2.不带参数的构造方法

源码解释:

/**
     * 构造函数二:无参的构造函数
     * 会将上述的静态的DEFAULTCAPACITY_EMPTY_ELEMENTDATA属性,赋值给elementData属性,
     * 也即我们用这种方法构造ArrayList的时候,内存中不会生成新的实例化数组,而是引用一个静态的空数组。
     */
    public ArrayList(){
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

3.带参数Collection的构造方法

源码解释:

参数c为一个Collection,Collection的实现类大概有以下几种常用类型:

  • List:元素可以重复的容器
  • Set: 元素不可重复的容器
  • Queue:结构是一个队列,先进先出
/**
     * 构造函数三:传递一个集合给ArrayList
     * @param c Collection集合
     */
    public ArrayList(Collection c){
        //首先会将集合转换成数组赋值给elementData,toArray方法有可能得到的不是Object[]类型
        elementData = c.toArray();
        if ((size = elementData.length) != 0){
            //判断转换后的elementData是否是Object[]类型
            if (elementData.getClass() != Object[].class){
                //如果不是Object[]类型,将它转换成Object[]类型
                elementData = Arrays.copyOf(elementData, size, Object[].class);
            }
        } else {
            //等于0,则将elementData赋值为EMPTY_ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

四、方法

1.trimToSize()

说明:将ArrayList的容量设置为当前size的大小。首先需要明确一个概念,ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。ArrayList每次都会预申请多一点空间,以便添加元素的时候不需要每次都进行扩容操作,例如我们的元素个数是10个,它申请的内存空间必定会大于10,即length>size,而这个方法就是把ArrayList的内存空间设置为size,去除没有用到的null值空间。这也就是我们为什么每次在获取数据长度是都是调用list.size()而不是list.length()。

源码解释:首先modCount是从类 java.util.AbstractList 继承的字段,这个字段主要是为了防止在多线程操作的情况下,List发生结构性的变化,什么意思呢?就是防止一个线程正在迭代,另外一个线程进行对List进行remove操作,这样当我们迭代到最后一个元素时,很明显此时List的最后一个元素为空,那么这时modCount就会告诉迭代器,让其抛出异常 ConcurrentModificationException。

如果没有这一个变量,那么系统肯定会报异常ArrayIndexOutOfBoundsException,这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该考虑的),为了避免出现这样的异常,定义了检查。

/**
     * 本质上是将数组的尾部空余的空间删除掉形成新数组
     * 新数组的length与size一致,节约空间
     */
    public void trimToSize() {
        //修改次数加1
        modCount++;
        //ArrayList的size就是ArrayList的元素个数,length是ArrayList申请的内容空间长度。
        if (size < elementData.length){
            //将elementData中空余的空间(包括null值)去除,例如:数组长度为10,其中只有前三个元素有值,其他为空,那么调用该方法之后,数组的长度变为3.
            elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
        }
    }

2.ensureCapacity()扩容

/**
     * public方法,用户可以自己扩容,
     * 确保当前数组的容量可以满足继续向数组中添加元素
     * @param minCapacity 所需的最小容量
     */
    public void ensureCapacity(int minCapacity){
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY;
        if (minCapacity > minExpand){
            //扩容
            ensureExplicitCapacity(minCapacity);
        }
    }

    /**
     * 私有方法,内部判断是否需要扩容
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity){
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

    /**
     * 判断是否需要扩容
     * 如果需要就调用grow方法,如果不需要,什么也不做
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0){
            //正在实行扩容的方法
            grow(minCapacity);
        }
        //如果minCapacity小于elementData数组的长度,说明无需扩容,什么也不做
    }


    /**
     * ArrayList的扩容,接收一个int类型参数,表示至少需要多少容量
     * @param minCapacity 数组所需最小容量
     */
    private void grow(int minCapacity) {
        //得到目前的容量
        int oldCapacity = elementData.length;
        //oldCapacity>>1表示除2取整数,该式子最终表示意思为newCapacity大于为oldCapacity的1.5倍数
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 下面的if处理等价 newCapacity = Math.Max(newCapacity,minCapacity);
        if (newCapacity - minCapacity < 0){
            newCapacity = minCapacity;
        }
        // 判断newCapacity是否超过最大值
        if (newCapacity - MAX_ARRAY_SIZE > 0){
            newCapacity = hugeCapacity(minCapacity);
        }
        //执行Arrays.copyOf方法,传递原数组与新数组长度,由Arrays内部创建数组返回并赋给elementData
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    /*
     * 求出最大的容量值,首先判断minCapacity是否已经溢出了,溢出了就直接抛出OOM
     * 否则就去判断minCapacity 是否大于 MAX_ARRAY_SIZE
     * 大于返回 Integer.MAX_VALUE ,不大于 返回MAX_ARRAY_SIZE
    */
    private static int hugeCapacity(int minCapacity){
        if (minCapacity < 0){
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

3.size()获取ArrayList的大小

//继承AbstractList所必须实装的方法,返回ArrayList的大小
    @Override
    public int size() {
        return size;
    }

4.isEmpty()判断数组是否为空

 /**
     * 判断数组是否为空
     * @return
     */
    public boolean isEmpty(){
        return size == 0;
    }

5.contains(Object o)判断是否包含对象o

/**
     * 判断是否包含对象o
     * @return
     */
    public boolean contains(Object o){
        //调用indexOf()方法得到下标,存在则下标>=0,不存在为-1,即只要比较下标和0的大小即可。
        return indexOf(0) >= 0;
    }

6.indexOf(Object o)获取o在ArrayList中的下标位置

/**
     * 对象o在ArrayList中的下标位置,如果存在返回位置i,不存在返回-1
     * @param o
     * @return
     */
    public int indexOf(Object o ){
        //遍历ArrayList的大小,比较o和容器内的元素,若相等,则返回位置i,若遍历完都不相等,返回-1
        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;
    }

7.lastIndexOf(Object o)获取o在ArrayList中最后出现的下标位置

/**
     * 对象o在ArrayList中的出现的最后一个位置,如果存在返回位置i,不存在返回-1
     * @param o
     * @return
     */
    public int lastIndexOf(Object o){
        //和indexOf方法处理逻辑一样,这里是倒序查找
        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;
    }

8.toArray()

 /**
     * 将ArrayList转换成数组
     * @return
     */
    public Object[] toArray(){
        return Arrays.copyOf(elementData, size);
    }

9.toArray(T[] a)

/**
     * 如果传入数组的长度小于size,返回一个新的数组,大小为size,类型与传入数组相同。
     * 所传入数组长度与size相等,则将elementData复制到传入数组中并返回传入的数组。
     * 若传入数组长度大于size,除了复制elementData外,还将把返回数组的第size个元素置为空。
     * @param a
     * @param 
     * @return
     */
    public  T[] toArray(T[] a){
        if (a.length < size){
            return (T[])Arrays.copyOf(elementData, size, a.getClass());
        }
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size){
            a[size] = null;
        }
        return a;
    }

10.get(int index)

/**
     * 不需要检查index的快速访问元素,但是是包权限,只允许内部使用
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    E elementData(int index){
        return (E)elementData[index];
    }

    /**
     *  继承AbstractList所必须实装的方法
     *  通过下标快速访问来返回元素
     * @param index
     * @return
     */
    @Override
    public E get(int index){
        rangeCheck(index);
        return elementData(index);
    }

    /**
     * 测试index是否越界
     * @param index
     */
    private void rangeCheck(int index) {
        // 如果下标超过ArrayList的数组长度
        if (index >= size){
            // 抛出IndexOutOfBoundsException异常
            throw new IndexOutOfBoundsException((outOfBoundsMsg(index)));
        }
    }

    /**
     * 抛出IndexOutOfBoundsException异常的message
     * @param index
     * @return
     */
    private java.lang.String outOfBoundsMsg(int index){
        return "Index: " + index + ", Size: " + size;
    }

11.set(int index, E element)

/**
     * 设置index位置的元素值了element,返回该位置的之前的值
     * @param index
     * @param element
     * @return
     */
    public E set(int index, E element){
        //先判断下标是否越界
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

12.add(E e)

/**
     * 添加元素
     * @param e
     * @return
     */
    public boolean add(E e){
        //判断是否需要扩容
        ensureCapacityInternal(size + 1);
        elementData[size] = e;
        return true;
    }

13.add(int index, E element)


/**
     * 添加元素,指定添加位置
     * @param element
     * @return
     */
    public void add(int index, E element){
        // 判断index是否越界
        rangeCheckForAdd(index);
        //判断是否需要扩容
        ensureCapacityInternal(size + 1);
        // 将elementData从index位置开始,复制到elementData的index+1开始的连续空间
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        // 在elementData的index位置赋值element
        elementData[index] = element;
        size++;
    }

    /**
     * 判断index是否越界
     * @param index
     */
    private void rangeCheckForAdd(int index){
        if (index > size || index < 0){
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }

14.remove(int index)

/**
     * 指定下标删除元素
     * @param index
     * @return
     */
    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);
        }
        //清除最后一个元素的引用,让GC来清理内存空间,因为原来的元素以及被删除了
        elementData[--size] = null;
        return oldValue;
    }

15.remove(Object o)

/**
     * 删除指定元素
     * @param o
     * @return
     */
    public boolean remove(Object o){
        if (o == null) {//删除null元素
            for (int index = 0; index < size; index++)//迭代ArrayList
                if (elementData[index] == null) {//如果在size之前的位置有存在空元素
                    fastRemove(index);//则快速删除(所谓快速删除,就是不去做越界检查以及不返回结果,完全给本类自己使用的private方法)
                    return true;
                }
        } else {//删除非空元素,与删除null元素逻辑相同
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {//此处使用equals方法来进行比较,所以在使用remove(Object o)的时候,要考虑是否重写了equals方法
                    fastRemove(index);//fastRemove也是会移动数组的,如果有删除重复元素的时候,效率很低
                    return true;
                }
        }
        return false;
    }

    /**
     * 快速删除
     * 不做index检查,不返回结果,只允许内部使用
     * @param index
     * @return
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0){
            System.arraycopy(elementData, index+1, elementData, index, numMoved);
        }
        elementData[--size] = null; // clear to let GC do its work
    }

16.clear()

/**
     * 清除数组,所有元素置为null
     */
    public void clear(){
        modCount++;
        for (int i = 0; i < size; i++){
            elementData[i] = null;
        }
        size = 0;
    }

17.addAll(Collection c)

/**
     * 一次性add多个元素,接受参数为集合类型
     * @param c
     * @return
     */
    public boolean addAll(Collection c){
        // 将c转换为数组a
        Object[] a = c.toArray();
        // 获取a占的内存空间长度赋值给numNew
        int numNew = a.length;
        //扩容
        ensureCapacityInternal(size + numNew);
        //将a数组插入到elementData的size位置
        System.arraycopy(c, 0, elementData, size, numNew);
        // 将size增加numNew 
        size += numNew;
        // 如果c为空,返回false,c不为空,返回true
        return numNew != 0;
    }

18.addAll(int index, Collection c)

/**
     * 指定index位置插入多个元素,原来位置的元素依次向后移动
     * index不能大于size,如果大于size会产生数组越界
     * @param index
     * @param c
     * @return
     */
    public boolean addAll(int index, Collection c){
        rangeCheckForAdd(index);
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);
        int numMoved = size - numNew;
        if (numMoved > 0){
            System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
        }
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

19.removeRange(int fromIndex, int toIndex)

/**
     * 范围删除,删除从fromIndex~toIndex,包含fromIndex,不包含toIndex
     * @param fromIndex
     * @param toIndex
     */
    protected void removeRange(int fromIndex, int toIndex){
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
        int newSize = size - (toIndex - fromIndex);
        for (int i = newSize; i < size; i ++){
            elementData[i] = null;
        }
        size = newSize;
    }

20.removeAll(Collection c)

/**
     * 一次性删除多个元素
     * @param c
     * @return
     */
    public boolean removeAll(Collection c){
        //判断c是否为空,为空抛出异常
        Objects.requireNonNull(c);
        //批量删除
        return batchRemove(c, false);
    }

21.retainAll(Collection c)

/**
     * 保留当前容器与c的并集,并返回
     * 和removeAll相反,仅保留c中所有的元素
     * @param c
     * @return
     */
    public boolean retainAll(Collection c){
        //判断c是否为空,为空抛出异常
        Objects.requireNonNull(c);
        //批量删除,注意第二个参数是TRUE
        return batchRemove(c, true);
    }

22.batchRemove(Collection c, boolean complement)

/**
     * 私有方法,根据complement值,将ArrayList中包含c中元素的元素删除或者保留
     * @param c
     * @param complement
     * @return
     */
    private boolean batchRemove(Collection c, boolean complement) {
        final Object[] elementData = this.elementData;
        int r = 0, w = 0;//一个读的index,一个是写的index
        boolean modified = false;
        try {
            for (; r < size; r++){
                if (c.contains(elementData[r]) == complement){
                    elementData[w++] = elementData[r];
                }
            }
        } finally {
            // 防止抛出异常导致上面r的右移过程没完成
            if (r != size){
                // 将r未右移完成的位置的元素赋值给w右边位置的元素
                System.arraycopy(elementData, r, elementData, w, size - r);
                // 修改w值增加size-r
                w += size - r;
            }
            // 如果有被覆盖掉的元素,则将w后面的元素都赋值为null ,方便gc回收
            if (w != size){
                for (int i = w; i < size; i++){
                    elementData[i] = null;
                }
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

23.writeObject(java.io.ObjectOutputStream s)

 /**
     * 保存数组实例的状态到一个流(即它序列化)
     * @param s
     */
    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i = 0; i

24.readObject(java.io.ObjectOutputStream s)

/**
     * 从一个流中读出数组实例的状态
     * @param s
     * @throws java.io.IOException
     * @throws ClassNotFoundException
     */
    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException,ClassNotFoundException{
        elementData = EMPTY_ELEMENTDATA;
        s.defaultReadObject();
        s.readInt();

        if (size > 0){
            ensureCapacityInternal(size);
            Object[] a = elementData;
            for(int i =0; i < size; i++){
                a[i] = s.readObject();
            }
        }
    }

内部类

[java]  view plain  copy
  1. (1)private class Itr implements Iterator  
  2. (2)private class ListItr extends Itr implements ListIterator  
  3. (3)private class SubList extends AbstractList implements RandomAccess  
  4. (4)static final class ArrayListSpliterator implements Spliterator  

总结:

  • ArrayList自己实现了序列化和反序列化的方法,因为它自己实现了 private void writeObject(java.io.ObjectOutputStream s)和 private void readObject(java.io.ObjectInputStream s) 方法
  • ArrayList基于数组方式实现,无容量的限制(会扩容)
  • 添加元素时可能要扩容(所以最好预判一下),删除元素时不会减少容量(若希望减少容量,trimToSize()),删除元素时,将删除掉的位置元素置为null,下次gc就会回收这些元素所占的内存空间
  • 线程不安全
  • add(int index, E element):添加元素到数组中指定位置的时候,需要将该位置及其后边所有的元素都整块向后复制一位
  • get(int index):获取指定位置上的元素时,可以通过索引直接获取(O(1))
  • remove(Object o)需要遍历数组
  • remove(int index)不需要遍历数组,只需判断index是否符合条件即可,效率比remove(Object o)高
  • contains(E)需要遍历数组
  • 使用iterator遍历可能会引发多线程异常

五、参考

1:https://blog.csdn.net/ljcitworld/article/details/52041836

2:https://blog.csdn.net/fighterandknight/article/details/61240861

你可能感兴趣的:(java,源码)