ArrayList源码浅析

 

一、ArrayList简介

  ArrayList是Java集合类中使用最频繁的几个集合类之一,它的底层是可以动态增长和缩减的数组。先来看一下ArrayList的继承关系图:

  

  可以看到ArrayList的顶层集合类为Collection。ArrayList实现了List接口,继承了AbstracList类,提供了数组集合相关的增、删、改、查和遍历等功能;实现了RandomAccess接口,提供了随机访问的能力。

  因为ArrayList的底层实现是数组,所以其在在内存中是连续的一块内存,因此其根据数组下标随机访问的效率很高,插入和删除的效率较低。当集合中的元素数量超出数组容量的时候,便会进行扩容操作,扩容操作是ArrayList集合一个比较耗费性能的操作,因此如果我们提前可以预知数据的规模,应该在初始化的时候就直接确定ArrayList的大小,避免之后的扩容操作,提高效率。

 

二、源码浅析

1、属性和构造方法

    /**
     * 默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 初始容量为0的构造方法,设置数组容量为EMPTY_ELEMENTDATA
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 无参构造设置的数组大小
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储ArrayList元素的数组。
     */
    transient Object[] elementData; 

    /**
     * 数组大小
     */
    private int size;


    /**
     * 创建ArrayList实例时,设置初始容量
     */
    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);
        }
    }

    /**
     * 无参构造,缺省存储数组为一个空数组。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 以一个Collection实例初始化ArrayList
     */
    public ArrayList(Collection<? extends E> c) {
        //返回一个包含此集合中所有元素的数组。
        elementData = c.toArray();
        //collection长度不为 0 时,给 size 赋值
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            //当 c.toArray 出错的情况下没有返回 Object[] ,将 elementData 的类型指定为 Object[]
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

  构造方法执行完之后,ArrayList实例中的elementData和size属性就已经确定了。

2、新增元素及扩容

  
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }


    
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /**
     * 计算ArrayList实例的容量
     */
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //如果当前ArrayList实例大小为"空",那么但会缺省容量(10)或者minCapacity(当前size+1)中大的那个数
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //否则返回minCapacity(当前size+1)
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //如果calculateCapacity()方法返回的数组容量大于当前数组的容量,则扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


    /**
     * 执行扩容
     */
    private void grow(int minCapacity) {
        //oldCapacity指向当前数组容量大小
        int oldCapacity = elementData.length;
        //新容量扩增为以前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量比传入最小容量还小,那么将传入容量为新容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果新容量比最大数组大小还大,比较参数minCapacity,最终确定数据大小为Integer.MAX_VALUE或MAX_ARRAY_SIZE
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    /**
     *
     * 在此列表中的指定位置插入指定的元素。 插入,原来的向后重新排列
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }


    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();       //将集合转化为对象数组
        int numNew = a.length;          //记录新数组长度
        ensureCapacityInternal(size + numNew);  //对当前ArrayList进行扩容
        System.arraycopy(a, 0, elementData, size, numNew);  //将新数组的值copy到原数组之后,append
        size += numNew;
        return numNew != 0;
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;    //需要移动的元素个数
        if (numMoved > 0)
            //将index及之后的元素向后移动a.length的长度
            System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
        //将需要插入的集合c数组插入
        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

ArrayList的扩容流程图(请别介意图很low):

3、删除元素及缩容

    /**
     * 移除指定索引处的元素
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);    //取出index处的数据

        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

        return oldValue;
    }

    /**
     * 删除该元素在数组中第一次出现的位置上的数据。 如果有该元素返回true,如果false。
     */
    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;
    }


    /**
     * 跳过边界检查、不返回删除的数据
     */
    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
    }

    protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        //将原数组toIndex之后剩余的元素拷贝到从fromIndex处到numMoved处
        System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;  //多余元素置为null
        }
        size = newSize;
    }

    /**
     * 将此 ArrayList 实例的容量(底层数组length)调整为列表的当前大小(size)。
     */
    public void trimToSize() {
        modCount++;     //此列表已被结构修改的次数
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

4、查询及修改

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }


    public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

    E elementData(int index) {
        return (E) elementData[index];
    }

5、其他常用api

    /**
     * 返回当前ArrayList实例数组是否为空数组
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 返回list实例是否包含元素o
     */
    public boolean contains(Object o) {
        //可以判断obj是null的情况
        return indexOf(o) >= 0;
    }

    /**
     * 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
     */
    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;
    }


    /**
     * 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
     */
    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() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;     //将新实例的modCount(此列表已被结构修改的次数)置为0
            return v;           //返回此ArrayList实例的浅拷贝
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

三、ArrayList总结

1、ArrayList底层是数组,随机访问效率高、插入删除效率低,迭代效率一般。在尾部新增数据时因为不涉及数据的移动,效率并不低。

2、新增数据可能会导致数组扩容,降低性能。因此尽可能减少扩容次数,在初始化的时候就为数组指定一个合适的初始容量。

3、ArrayList中的所有方法都不是同步方法,线程非安全。在多线程编程中,需要注意这点。

4、ArrayList常用来与LinkedList和Vector相比较,它们之间的一些异同关系也需要掌握,这里就不赘述了。

 

posted @ 2018-12-03 19:57  风吹满楼  阅读(202)  评论(0编辑  收藏  举报