JAVA集合源码分析系列:ArrayList源码分析

345人阅读 评论(0) 收藏 举报
分类:

一.ArrayList简介

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支持序列化,能通过序列化去传输。

二.ArrayList源码解析

ArrayList包含了两个重要的对象:elementData 和 size。

(01) elementData 是”Object[]类型的数组”,它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。

(02) size 则是动态数组的实际大小。

1.构造函数

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);
        }
    }

这里可以看到,在初始化ArrayList的时候,我们可以给它传递一个参数initialCapacity,如果initialCapacity>0,这个参数就是elementData数组的初始大小。如果=0,系统会给elementData数组一个默认的大小

2.Add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

这个方法的大致意思是:当我们第一次new一个ArrayList的时候,size为0,我们执行add方法,minCapacity取的值为DEFAULT_CAPACITY(10),就是说如果我们在第一次new ArrayList的时候,如果我们不设定动态数组的初始大小,则库会给一个默认的大小10.

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

如果minCapacity大于elementData数组的长度,就需要对数组进行扩容,具体的扩容的方法是:grow方法,我们看一下grow方法的实现:

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

扩容时,有一个最大的限制:MAX_ARRAY_SIZE=Integer.MAX_VALUE - 8;根据以上的代码可以看出新的容量=3倍的旧容量。

至于具体的扩容方案:Arrays.copyOf(elementData, newCapacity);这个API的作用是:
copyOf()是系统自动在内部新建一个数组,调用arraycopy()将original内容复制到copy中去,并且长度为newLength。返回copy; 即将原数组拷贝到一个长度为newLength的新数组中,并返回该数组。

三.总结

通过源码的阅读,我们发现ArrayList的内部实现是数组,我们在大学学习《数据结构》的时候就知道,相比链表,数组的特点是查找方便,但是增加元素、移除元素比较慢(除非在最后的位置上操作)。我们可以根据具体的应用场景来选择合适的数据结构。

以下博客对数组和链表做了对比:

http://blog.csdn.net/u014082714/article/details/44259029

说明:

以上我阅读的是jdk1.8.0_65,不同版本可能实现有所差异不一样

http://www.cnblogs.com/skywang12345/p/3308556.html
http://www.jianshu.com/p/085a5ba2aca8

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    写给自己
    ○ 种一棵树最好的时间是十年前,其次是现在

    ○ 坚持输出,坚持书写,才可以持续成长

    ○ 所有美好事物的成长都是缓慢的

    ○ 既往不恋,未来不迎,当下不杂

    ○ 业精于勤,荒于嬉,行成于思,毁于随

    ○将军赶路 不追小兔

    ○不要拘泥于语言,同样也不要拘泥于行业,眼光放远一点

    ○ 如果某件事你做的不够好,不必介怀,因为以后的每一次每一天你都会做得越来越好

    ○ 此心不于事上磨,更于何处磨此心

    ○ 保持热情,保持求知欲

    ○ 千里之行,始于足下

    ○ 最怕你一生碌碌无为,还安慰自己平凡可贵。

    ○ 对于任何事,要保持自觉积极主动探索尝试。但是如果自己不积极认真地生活,不管得到什么样的回答都没有用。——解忧杂货店
    个人资料
    • 访问:648301次
    • 积分:49
    • 等级:
    • 排名:千里之外
    • 原创:288篇
    • 转载:50篇
    • 译文:0篇
    • 评论:114条
    个人简介