本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组。数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间用来进行元素的存储,由于它直接操作内存,所以数组的性能要比集合类更好一些,这是使用数组的一大优势。但是我们知道数组存在致命的缺陷,就是在初始化时必须指定数组大小,并且在后续操作中不能再更改数组的大小。在实际情况中我们遇到更多的是一开始并不知道要存放多少元素,而是希望容器能够自动的扩展它自身的容量以便能够存放更多的元素.ArrayList就能够很好的满足这样的需求,它能够自动扩展大小以适应存储元素的不断增加。它的底层是基于数组实现的,因此它具有数组的一些特点,例如查找修改快而插入删除慢。本篇我们将深入源码看看它是怎样对数组进行封装的。首先看看它的成员变量和三个主要的构造器。
//默认初始化容量 私有静态最终int DEFAULT_CAPACITY=10;//空对象数组 私有静态最终对象[]EMPTY_ELEMENTDATA=https://www.yisu.com/zixun/{};//对象数组 私人瞬时对象[]elementData;//集合元素个数 私人int大小;//传入初始容量的构造方法 公共ArrayList (int initialCapacity) { 超级(); 如果(initialCapacity <0) { 把新IllegalArgumentException(“非法容量:”+ initialCapacity); }//新建指定容量的对象类型数组 这一点。elementData=https://www.yisu.com/zixun/new对象(initialCapacity); }//不带参数的构造方法 公共ArrayList () { 超级();//将空的数组实例传给elementData 这一点。elementData=EMPTY_ELEMENTDATA; }//传入外部集合的构造方法 公共ArrayList(收集<?扩展E> c) {//持有传入集合的内部数组的引用 elementData=c.toArray ();//更新集合元素个数大小 大?elementData.length;//判断引用的数组类型,并将引用转换成对象数组引用 如果(elementData.getClass () !=Object [] . class) { elementData=数组。copyOf (elementData、大小、对象[]. class); } } >之前可以看到ArrayList的内部存储结构就是一个对象类型的数组,因此它可以存放任意类型的元素。在构造ArrayList的时候,如果传入初始大小那么它将新建一个指定容量数的对象组,如果不设置初始大小那么它将不会分配内存空间而是使用空的对象数组,在实际要放入元素时再进行内存分配。下面再看看它的增删改查方法。
//增(添加) 公共逻辑加(E E) {//添加前先检查是否需要拓展数组,此时数组长度最小尺寸为+ 1 ensureCapacityInternal(+ 1)大小;//将元素添加到数组末尾 elementData(大小+ +)=e; 返回true; }//增(插入) 公共空间添加(int指数E元素){//插入位置范围检查 rangeCheckForAdd(指数);//检查是否需要扩容 ensureCapacityInternal(+ 1)大小;//挪动插入位置后面的元素 系统。arraycopy (elementData elementData、索引,索引+ 1,大小-指数);//在要插入的位置赋上新值 elementData(指数)=元素; 大小+ +; }//删 公共E删除(int指数){//索引不能大于大小 rangeCheck(指数); modCount + +; E oldValue=https://www.yisu.com/zixun/elementData(指数); int numMoved=大小-指数- 1; 如果(numMoved> 0) {//将索引后面的元素向前挪动一位 系统。arraycopy (elementData、索引+ 1 elementData指数,numMoved); }//置空引用 elementData[——大小]=零; 返回oldValue; }//改 公共E组(int指数E元素){//索引不能大于大小 rangeCheck(指数); E oldValue=elementData(指数);//替换成新元素 elementData(指数)=元素; 返回oldValue; }//查 公共E (int指数){//索引不能大于大小 rangeCheck(指数);//返回指定位置元素 返回elementData(指数); } >之前每次添加一个元素到集合中都会先检查容量是否足够,否则就进行扩容,扩容的细节下面会讲到。我们先看具体增删改查要注意的地方。
增(添加):仅是将这个元素添加到末尾。操作快速。
增(插入):由于需要移动插入位置后面的元素,并且涉及数组的复制,所以操作较慢。
删:由于需要将删除位置后面的元素向前挪动,也会设计数组复制,所以操作较慢。
改:直接对指定位置元素进行修改,不涉及元素挪动和数组复制,操作快速。
查:直接返回指定下标的数组元素,操作快速。Java集合系列之ArrayList源码分析