array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 ArrayList源码浅析 - 爱码网

 

一、ArrayList简介

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

  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):

ArrayList源码浅析

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相比较,它们之间的一些异同关系也需要掌握,这里就不赘述了。

 

相关文章: