一、前言
分析了Map中主要的类之后,下面我们来分析Collection下面几种常见的类,如ArrayList、LinkedList、HashSet、TreeSet等。下面通过JDK源码来一起分析ArrayList底层是如何实现的。(PS:把JVM看完了之后终于可以有成片的时间来阅读源码了,感觉简直不能更爽)。
二、ArrayList数据结构
分析一个类的时候,数据结构往往是它的灵魂所在,理解底层的数据结构其实就理解了该类的实现思路,具体的实现细节再具体分析。
ArrayList的数据结构如下:
说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。下面我们来分析通过数组是如何保证库函数的正确实现的。
三、ArrayList源码分析
3.1 类的继承关系
public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable
说明:ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。
3.2 类的属性
View Code
说明:类的属性中核心的属性为elementData,类型为Object[],用于存放实际元素,并且被标记为transient,也就意味着在序列化的时候,此字段是不会被序列化的。
3.3 类的构造函数
1. ArrayList(int)型构造函数
View Code
说明:指定elementData数组的大小,不允许初始化大小小于0,否则抛出异常。
2. ArrayList()型构造函数
View Code
说明:当未指定初始化大小时,会给elementData赋值为空集合。
3. ArrayList(Collection extends E>)型构造函数
View Code
说明:当传递的参数为集合类型时,会把集合类型转化为数组类型,并赋值给elementData。
3.4 核心函数分析
1. add函数
View Code
说明:在add函数我们发现还有其他的函数ensureCapacityInternal,此函数可以理解为确保elementData数组有合适的大小。ensureCapacityInternal的具体函数如下
View Code
说明:在ensureCapacityInternal函数中我们又发现了ensureExplicitCapacity函数,这个函数也是为了确保elemenData数组有合适的大小。ensureExplicitCapacity的具体函数如下
View Code
说明:在ensureExplicitCapacity函数我们又发现了grow函数,grow函数才会对数组进行扩容,ensureCapacityInternal、ensureExplicitCapacity都只是过程,最后完成实际扩容操作还是得看grow函数,grow函数的具体函数如下
View Code
说明:正常情况下会扩容1.5倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值。
当我们调用add方法时,实际上的函数调用如下
说明:程序调用add,实际上还会进行一系列调用,可能会调用到grow,grow可能会调用hugeCapacity。
下面通过两种方式给出调用add的例子,并分析最后的elementData数组的大小。
示例一(只给出了会影响到最终结果的核心代码)
Listlists = new ArrayList (); lists.add(8);
说明:初始化lists大小为0,调用的ArrayList()型构造函数,那么在调用lists.add(8)方法时,会经过怎样的步骤呢?下图给出了该程序执行过程和最初与最后的elementData的大小
说明:我们可以看到,在add方法之前开始elementData = {};调用add方法时会继续调用,直至grow,最后elementData的大小变为10,之后再返回到add函数,把8放在elementData[0]中。
示例二核心代码如下
Listlists = new ArrayList (6); lists.add(8);
说明:调用的ArrayList(int)型构造函数,那么elementData被初始化为大小为6的Object数组,在调用add(8)方法时,具体的步骤如下:
说明:我们可以知道,在调用add方法之前,elementData的大小已经为6,之后再进行传递,不会进行扩容处理。
2. set函数
View Code
说明:设定指定下标索引的元素值。
3. indexOf函数
View Code
说明:从头开始查找与指定元素相等的元素,注意,是可以查找null元素的,意味着ArrayList中可以存放null元素的。与此函数对应的lastIndexOf,表示从尾部开始查找。
4. get函数
View Code
说明:get函数会检查索引值是否合法(只检查是否大于size,而没有检查是否小于0),值得注意的是,在get函数中存在element函数,element函数用于返回具体的元素,具体函数如下
View Code
说明:返回的值都经过了向下转型(Object -> E),这些是对我们应用程序屏蔽的小细节。
5. remove函数
View Code
说明:remove函数用户移除指定下标的元素,此时会把指定下标到数组末尾的元素向前移动一个单位,并且会把数组最后一个元素设置为null,这样是为了方便之后将整个数组不被使用时,会被GC,可以作为小的技巧使用。
四、总结
ArrayList有其特殊的应用场景,与LinkedList相对应。其优点是随机读取,缺点是插入元素时需要移动大量元素,效率不太高。至此,ArrayList的源码分析就到这里,总体来说,ArrayList的底层还是很简单的,谢谢各位园友的观看~
作者:leesf 掌控之中,才会成功;掌控之外,注定失败。
出处:http://www.cnblogs.com/leesf456/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。