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

ArrayList源码分析_0

时间:2023-04-01 21:50:12 Java

在普通的Java中,需要用到列表来存储数据,大部分时候可以使用ArrayList。比如Mybatis查询数据列表,返回的列表是ArrayList。许多数据存储也使用ArrayList。jdk版本:1.8ArrayList基于变长数组实现,允许添加空值,可以根据下标快速查询数据。一旦数组被初始化,大小就确定了。如果需要添加数据,则需要复制数据。这些操作很耗时。数组拷贝ArrayList数组的扩容、增删改查都需要用到数组的拷贝。主要用到下面两个方法:System.arraycopyArrays.copyOfSystem.arraycopySystem.arraycopy()是Java原生的静态方法。复制到目标数组。参数说明:srcsourcearraysrcPossourcearraycopystartindexdesttargetarraydestPoscopytotargetarraystartindexlengthlengthcopyelementsnumberofcopysrcsourcearray,startindexissrcPos,numberislength,copytodest目标数组有起始索引目标位置例如:int[]srcArray=newint[]{1,2,3,4,5,6};int[]desArray=newint[5];System.arraycopy(srcArray,1,desArray,2,3);System.out.println("desArray:"+Arrays.toString(desArray));输出:[0,0,2,3,4]Arrays.copyOfArrays.copyOf实际上调用了System.arraycopy方法:publicstaticint[]copyOf(int[]original,intnewLength){int[]copy=newint[新长度];系统。arraycopy(原件,0,副本,0,Math.min(原件。长度,newLength));返回副本;}首先创建一个新的数组副本,并将原来的数组全部复制到副本数组中。主字段://底层数组transientObject[]elementData;//数组大小privateintsize;//默认空数组privatestaticfinalObject[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};ArrayList是基于数组的实现,elementData是底层数组。size是记录数组的数量。构造方法ArrayList()publicArrayList(){this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}无参构造方法,创建数据直接赋空数组。ArrayList(intinitialCapacity)分配初始容量initialCapacity:publicArrayList(intinitialCapacity){if(initialCapacity>0){this.elementData=newObject[initialCapacity];}elseif(initialCapacity==0){this.elementData=EMPTY_ELEMENTDATA;}else{thrownewIllegalArgumentException("非法容量:"+initialCapacity);}}初始化容量initialCapacity大于零,创建一个长度为initialCapacity的新数组。将容量initialCapacity初始化为零,并创建一个空数组。添加数据添加数据有两种方式:add(Ee)直接在数组末尾添加。add(intindex,Eelement)根据下标添加数据。add(Ee)在列表末尾添加数据:/***将指定元素追加到此列表的末尾。*/publicbooleanadd(Ee){ensureCapacityInternal(size+1);//递增modCount!!元素数据[size++]=e;returntrue;}ensureCapacityInternal判断添加数据后的容量是否足够,如果不够则进行扩容。privatevoidensureCapacityInternal(intminCapacity){ensureExplicitCapacity(calculateCapacity(elementData,minCapacity));}privatestaticintcalculateCapacity(Object[]elementData,intminCapacity){if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTCapITY){返回minCFA数学。;}returnminCapacity;}privatevoidensureExplicitCapacity(intminCapacity){modCount++;//溢出意识代码if(minCapacity-elementData.length>0)grow(minCapacity);}ensureCapacityInternal用于判断当前容量是否足够存储新的数据,如果数据不够,扩容。calculateCapacity计算容量,如果数组为空,返回minCapacity和10的最大值,否则返回minCapacity。此方法返回数组的大小。调用无参构造函数ArrayList(),然后调用add()方法。首先,阵列容量将变为10。这就是为什么在无参构造方法ArrayList()上有注解Constructsanemptylistwithaninitialcapacityoften.ensureExplicitCapacity。判断所需容量minCapacity大于当前数组elementData.length的长度,也会调用扩容处理。方法:privatevoidgrow(intminCapacity){//溢出意识代码intoldCapacity=elementData.length;//新长度是原来长度的1.5倍intnewCapacity=oldCapacity+(oldCapacity>>1);if(newCapacity-minCapacity<0)//新的长度小于要求的长度,赋给新的长度newCapacity=minCapacity;如果(newCapacity-MAX_ARRAY_SIZE>0)newCapacity=hugeCapacity(minCapacity);//复制数组到新的长度数组elementData=Arrays.copyOf(elementData,newCapacity);}group主要做了两件事:扩容到原来长度的1.5倍,复制数组到新的长度数组,判断是否展开,直接在size位置赋值要增加的元素,然后size会自动增加。元素数据[大小++]=e;add(Ee)过程总结判断数据容量是否足够,如果是空数组,返回10,其他容量返回当前数组大小+1。返回的容量与当前数组长度比较,小于那是为了扩容。扩展长度为原数组长度的1.5倍,将数组赋值给长度为原数组1.5倍的新数组。数组末尾的大小分配。add(intindex,Eelement)这个添加的数据就是在指定的下标处添加数据。publicvoidadd(intindex,Eelement){//下标是否越界rangeCheckForAdd(index);//判断是否扩展ensureCapacityInternal(size+1);//递增modCount!!//将数据从i复制到size到i+1到size+1System.arraycopy(elementData,index,elementData,index+1,size-index);元素数据[索引]=元素;size++;}add(intindex,Eelement)在索引下标添加数据,先判断索引是否在0到size之间。判断是否扩容,需要扩容,扩容1.5倍。数据迁移,将索引后的所有数据向后移动一位。赋值索引下标数据。获取数据获取数据只能通过数组下标获取数据get(intindex):publicEget(intindex){//检查数组是否超出范围rangeCheck(index);returnelementData(index);}@SuppressWarnings("unchecked")EelementData(intindex){//通过下标获取数据return(E)elementData[index];}这里获取数据比较简单:检查下标是否超过数组的范围。通过订阅访问数据。删除数据remove(Objecto)删除列表中的第一个匹配元素:publicbooleanremove(Objecto){if(o==null){for(intindex=0;index0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=空;//cleartogetGCtowork}移动数组数据,index+1及后续数据向前移动一位,最后一位size-1赋值null。remove(intindex)根据索引下标删除数据。publicEremove(intindex){//是否越界rangeCheck(index);模数++;EoldValue=elementData(index);intnumMoved=大小-索引-1;如果(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=空;//明确让GC干活returnoldValue;}总结一下数组的扩容、增删改查都需要用到System.arraycopy方法,System.arraycopy是将起始索引为srcPos、编号为length的src源数组复制到起始索引为destPos的dest目的数组。ArrayList主要是基于Object[]elementData动态数组实现的。调用构造函数,如果没有参数就赋一个空数组,如果有初始容量就赋一个初始容量。add添加一个数组,首先判断是否需要扩容,如果需要扩容则将长度扩容为原来的1.5倍。add(Ee)在大小分配中添加数据add(intindex,Eelement)将数组从索引向后移动一位,并将新数据添加到索引。获取数据get(intindex)利用数组的特殊性,根据下标获取数据。remove(intindex)将索引之后的所有数据向前移动一位,并将size-1分配给null。