当前位置: 首页 > 后端技术 > Java

ArrayList源码分析

时间:2023-04-02 00:58:20 Java

简介:ArrayList是我们开发中最常用的集合,作为使用频率很高的一个类,我们不妨读读源码说说。前言ArrayList是我们开发中最常用的集合,作为一个使用频率很高的类,我们不妨阅读一下源码来谈谈。ArrayList的继承关系介绍如下:AaaryList主要实现了List接口,标记为Serializable、CloneAble、RandomAccess。几个重要的成员变量/***默认容量*/privatestaticfinalintDEFAULT_CAPACITY=10;/***空实例的共享空数组实例。*/privatestaticfinalObject[]EMPTY_ELEMENTDATA={};/***默认大小的空实例的共享空数组实例。我们将其与空元素数据区分开来,以查看在添加第一个元素时要膨胀多少。*/privatestaticfinalObject[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};/***用于存储ArrayList元素的数组缓冲区。ArrayList的容量就是这个数组缓冲区的长度。*/transientObject[]elementData;//非私有以简化嵌套类访问/***ArrayList的大小(它包含的元素数)。*/私有整数大小;数据结构ArrayList的底层是一个数组,数组会随着数据的增长而膨胀。数组扩容就是新建一个大容量数组,然后将旧数组上的数据复制到新数组中。扩展将在后面详细解释。因为是数组,所以支持随机访问,是有序的。常用方法ArrayList()无参数构造方法publicArrayList(){this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}将数组初始化为空数组。与空元素数据区分开来,知道在添加第一个元素时要膨胀多少。add(Ee)添加元素将指定元素附加到此列表的末尾publicbooleanadd(Ee){ensureCapacityInternal(size+1);//递增modCount!!元素数据[大小++]=e;returntrue;}privatestaticintcalculateCapacity(Object[]elementData,intminCapacity){if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){返回Math.max(DEFAULT_CAPACITY,minCapacity);}returnminCapacity;}privatevoidensureCapacityInternal(intminCapacityExpcale(CapacityExpcalate){ensureminCapacity));}privatevoidensureExplicitCapacity(intminCapacity){modCount++;//溢出意识代码if(minCapacity-elementData.length>0)grow(minCapacity);}添加元素时,会先检查是否超出容量,如果超过,则需要扩容。第一次添加元素时,大小默认为0,会计算一个最小容量minCapacity。如果创建时没有参数构造,默认容量为10,Math.max(DEFAULT_CAPACITY,minCapacity),这里传入的minCapacity为0,所以取一个较大的10。如果计算出的最小容量大于原来的capacityminCapacity-elementData.length>0,将进行扩容。privatevoidgrow(intminCapacity){//溢出意识代码intoldCapacity=elementData.length;intnewCapacity=oldCapacity+(oldCapacity>>1);如果(newCapacity-minCapacity<0)newCapacity=minCapacity;如果(newCapacity-MAX_ARRAY_SIZE>0)newCapacity=hugeCapacity(minCapacity);//minCapacity通常接近大小,所以这是一个胜利:elementData=Arrays.copyOf(elementData,newCapacity);}扩容算法是扩容到旧容量的1.5倍,如果扩容后的容量仍然小于要求的最小容量minCapacity,则新容量为最小容量。如果扩展后的大小超过最大容量,则会执行以下操作privatestaticinthugeCapacity(intminCapacity){if(minCapacity<0)//overflowthrownewOutOfMemoryError();返回(最小容量>MAX_ARRAY_SIZE)?Integer.MAX_VALUE:MAX_ARRAY_SIZE;}计算完扩容后,扩容,即创建一个新的数组并初始化为新的容量,然后将旧的元素复制到新的数组中。elementData=Arrays.copyOf(elementData,newCapacity);publicstaticT[]copyOf(T[]original,intnewLength){return(T[])copyOf(original,newLength,original.getClass());}publicstaticT[]copyOf(U[]original,intnewLength,ClassnewType){@SuppressWarnings("unchecked")T[]copy=((Object)newType==(对象)对象[].类)?(T[])新对象[newLength]:(T[])Array.newInstance(newType.getComponentType(),newLength);System.arraycopy(original,0,copy,0,Math.min(original.length,newLength));返回副本;}为什么不能在forEach中修改列表ArrayList在添加或删除元素时会执行ArrayList的父类AbstractList中定义的modCount++modCount。/***此列表在结构上被修改的次数。结构修改是指那些改变列表大小的修改,或者以持续迭代可能产生不正确结果的方式干扰列表的修改。迭代器和列表迭代器方法返回的迭代器和列表迭代器实现使用此字段。如果此字段的值意外更改,迭代器(或列表迭代器)将抛出ConcurrentModificationException以响应next、remove、previous、set或add操作。这提供了快速失败的行为,而不是在迭代期间面对并发修改时的非确定性行为。子类对这个字段的使用是可选的。如果一个子类希望提供失败的快速迭代器(和列表迭代器),那么它只需在add(int,E)和remove(int)方法(以及它重写的任何其他导致列表结构修改的方法)中这样做)添加此字段。对add(int,E)或remove(int)的一次调用只能将一个添加到该字段,否则迭代器(和列表迭代器)将抛出一个虚假的ConcurrentModificationException。如果实现不希望提供快速失败的迭代器,则它们可以省略此字段。*/protectedtransientintmodCount=0;然后我们看一下forEach的实现。@OverridepublicvoidforEach(Consumeraction){Objects.requireNonNull(action);最终intexpectedModCount=modCount;@SuppressWarnings("unchecked")finalE[]elementData=(E[])this.elementData;finalintsize=this.size;对于(inti=0;modCount==expectedModCount&&i