当前位置: 首页 > 科技观察

面试角度分析ArrayList源码

时间:2023-03-20 18:19:12 科技观察

ArrayList类图如下:ArrayList底层由数组实现,特点是大小固定,而ArrayList实现动态扩展。ArrayList的一些变量如下,下面的分析中会用到这些变量。/***默认容量*/privatestaticfinalintDEFAULT_CAPACITY=10;/***空对象数组*/privatestaticfinalObject[]EMPTY_ELEMENTDATA={};/***无参数构造函数创建的空数组*/privatestaticfinalObject[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA={};/***存放数据的数组缓存变量*/transientObject[]elementData;/***元素个数*/privateintsize;1.初始化ArrayList初始化ArrayList一般使用以下两个构造函数1.1无参构造函数初始化ArrayList如果没有指定大小,将创建一个空数组。publicArrayList(){this.elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}1.2指定数组大小的构造函数创建一个估计大小的数组。指定大小后,只指定数组初始值的大小,不影响后续扩容。指定的好处是可以节省内存和时间开销。publicArrayList(initialCapacity){if(initialCapacity>0){this.elementData=newObject[initialCapacity];}elseif(initialCapacity==0){this.elementData=EMPTY_ELEMENTDATA;}else{thrownewIllegalArgumentException("IllegalCapacity:"+initialCapacity);}}两次添加元素,动态扩展ArrayList.add(Ee)源码:publicbooleanadd(Ee){ensureCapacityInternal(size+1);//IncrementsmodCount!!elementData[size++]=e;returntrue;}add()inelementData[size++]=e很好理解,就是把元素插入到size位置,然后size++,我们重点说ensureCapacityInternal(size+1)方法;privatevoidensureCapacityInternal(intminCapacity){if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){minCapacity=数学。max(DEFAULT_CAPACITY,minCapacity);}ensureExplicitCapacity(minCapacity);}ensureCapacityInternal()方法判断缓存变量elementData是否为空,即判断是否是第一次添加元素,如果是第一次添加一个元素,设置初始大小为默认容量10,否则为传入的参数。该方法的目的是获取数组初始容量。获取初始容量后,调用ensureExplicitCapacity(minCapacity)方法;privatevoidensureExplicitCapacity(intminCapacity){modCount++;//overflow-consciouscodeif(minCapacity-elementData.length>0)grow(minCapacity);}ensureExplicitCapacity(minCapacity)方法用于判断是否需要扩容,如果是第一次添加元素此时minCapacity为10,elementData容量为0,需要扩容。调用grow(minCapacity)方法。//数组最大容量privatestaticfinalintMAX_ARRAY_SIZE=Integer.MAX_VALUE-8;privatevoidgrow(intminCapacity){//overflow-consciouscodeintoldCapacity=elementData.length;//扩展大小为原数组长度的1.5倍intnewCapacity=oldCapacity+(oldCapacity>>1);//扩展容量小于需要扩展的长度,则使用需要扩展的容量if(newCapacity-minCapacity<0)newCapacity=minCapacity;//扩展容量大于最大数组长度,然后使用最大整数长度if(newCapacity-MAX_ARRAY_SIZE>0)newCapacity=hugeCapacity(minCapacity);//minCapacityisusuallyclosetosize,所以thisisawin:elementData=Arrays.copyOf(elementData,newCapacity);}grow(minCapacity)方法扩展数组,扩展大小为原数组的1.5倍,如果计算出来的扩展容量小于需要的容量,扩展大小就是需要的容量。如果扩展容量大于数组的最大容量,则调用hugeCapacity(minCapacity)方法将数组扩展到整数的最大长度,然后将elemetData数组指向新扩展的内存空间,将元素复制到新空间。当需要的采集容量特别大时,将容量扩大1.5倍会消耗大量空间,所以建议在初始化时预估一个容量。三种删除元素ArrayList提供了两种删除元素的方式,可以通过索引删除,也可以通过元素删除。两次删除是相似的。删除一个元素后,后面的元素一次一个地向前移动。ArrayList.remove(intindex)源代码:publicEremove(intindex){rangeCheck(index);modCount++;EoldValue=elementData(index);intnumMoved=size-index-1;if(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);elementData[--size]=null;//cleartoletGCdoitsworkreturnoldValue;}删除元素时,会先判断索引是否大于ArrayList的大小,如果索引范围正确,然后对索引位置的下一个元素给索引位置赋值,将ArrayList的大小减-1,最后返回移除的元素。运行图如下。如果我想去掉索引为1的元素:4.总结ArrayList底层是用数组实现的,可以动态扩展。展开尺寸是原来尺寸的1.5倍。比较浪费空间,所以建议在初始化的时候预估一下数组的大小。ArrayList允许插入重复值和空值。ArrayList实现了RandomAccess接口,支持快速随机访问,即可以通过索引快速找到一个元素,所以遍历时使用for循环效率更高。ArrayList是线程不安全的。可以通过Collections.synchronizedList转换成线程安全的集合,但一般不用。Vector和CopyOnWriteArrayList是线程安全的。Vector性能一般,逐渐被CopyOnWriteArrayList取代。本文转载自微信公众号“Java之旅”,可通过以下二维码关注。转载本文请联系Java之旅公众号。