无论是程序员的工作、学习,还是生活中的事情。你可以遵循这样一个原则:“简单的事情反复做,正确的事情反复做”。这样的努力会让你走在正确的道路上,少走很多弯路。从年轻司机到老司机。通过上一节,你应该已经掌握了ArrayList的扩展原理、System.arrayCopy的方法,以及阅读源码的一些思路和方法。这部分更多的是练习你所学的思想和方法。带你快速接触ArrayList其他常用方法的源码原理,看看它们的一些亮点。本节还可以让您简要了解fail-fast。机制,前面的modCount是干什么的。熟路的,扫一扫ArrayList的set和get方法。首先,您需要修改您的演示,如下所示:importjava.util.ArrayList;导入java.util.List;公共类ArrayListDemo{publicstaticvoidmain(String[]args){ListhostList=newArrayList<>();hostList.add("host1");hostList.add("host2");hostList.add("host3");System.out.println(hostList.set(1,"host4"));System.out.println(hostList.get(1));}}上面的代码,假设你通过add方法将3个host主机地址添加到hostList中。然后用set方法替换位置1的内容,并打印返回值。然后调用get方法获取位置1的元素,检查是否替换成功。上面的逻辑代码如图所示:这里需要说明的是,运维其实有一个原则,就是在运行完命令和脚本之后,一定要检查!查看!比如这里设置后,必须get才能看到。其实不仅仅是运维,很多情况下你都应该这样做,比如在线回测,SQL执行后检查,自测代码等等……你一定要牢记这个思想,举一反三从一个实例。话不多说,直接看源码,首先是set方法:publicEset(intindex,Eelement){rangeCheck(index);EoldValue=elementData(index);元素数据[索引]=元素;returnoldValue;}EelementData(intindex){return(E)elementData[index];}privatevoidrangeCheck(intindex){if(index>=size)thrownewIndexOutOfBoundsException(outOfBoundsMsg(index));}私有字符串outOfBoundsMsg(intindex){return"Index:"+index+",Size:"+size;}从注释或者API用法可以知道set方法的作用是替换某个位置的元素。通过源码可以看到set方法的来龙去脉:第一步rangeCheck很明显是范围检查,是一个验证动作;第二步是elementData(intindex)方法,通过数组下标获取元素,基本的数组操作,通过oldValue记录原始值;第三步,通过数组下标elementData[index]=element;进行赋值操作,最后返回之前记录的oldValue。其实这个源码很简单。这里更深刻的体现了ArrayList底层使用数组的原理。如果自己手写自定义List,可以参考这个思路。源码逻辑如下图所示:接下来我们快速看一下get方法:publicEget(intindex){rangeCheck(index);返回元素数据(索引);可以看到get方法的上下文比较简单,就是对范围进行检查、验证,然后使用基本的数组操作,通过数组下标获取元素。这里值得一提的是,JDK源码封装的方法不会太长,很清晰,可以复用。这种编码风格值得借鉴。但是也不能太精简,可读性会降低。JDK有这个问题。这也是因为大多数JAVA高手都喜欢极其精简的代码,这也是可以理解的。至此,set和get的源码逻辑如下图所示:##换汤不换药,相信remove系列的方法会越来越熟练,与时俱进在你看过add、get、set等方法之后。下面我们来看看ArrayList的remove系列方法。其实源码的底层原理还是和System.arraycopy一样的。remove系统方法如下图所示:上面是ArrayList中的remove方法,例子就不写了。相信大家可以直接阅读源码。publicEremove(intindex){rangeCheck(index);模数++;EoldValue=elementData(index);intnumMoved=大小-索引-1;如果(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=空;//明确让GC完成它的工作returnoldValue;}上面的上下文已经很清楚了,关键的两行是计算移动元素的个数,并把原数组元素上的元素复制到原数组中。你应该已经知道其余几行的作用,所以我不会在这里重复它们。源码逻辑如下图所示:System.arraycopy复制一般不好理解,举个例子让大家更容易理解:这句话大家应该很熟悉了,现在需要搬家了3个元素从原数组位置2到目标数组,从目标数组的位置1开始覆盖。这里source和target都是自己,结果会变成elementData[0,2,3,4,4]。去掉源码最后一句elementData[--size]=null;数组会变成elementData[0,2,3,4,null],让GC帮忙回收null值,size--,数组大小减1。remove(intindex)的方法是不是很简单?之后可以看看remove(Objecto)方法和他的区别:publicbooleanremove(Objecto){if(o==null){for(intindex=0;indexhostList=newArrayList<>();hostList.add("host1");hostList.add("host2");hostList.add("host3");hostList.add("host2");hostList.add(null);hostList.add(null);System.out.println("删除前:"+hostList);hostList.remove("host2");//只有第一个匹配的元素会被移除hostList.remove(null);//只删除第一个匹配到的元素System.out.println("Afterdeletion:"+hostList);}从输出结果可以知道,只删除第一个满足条件的元素。使用时应注意这一点。如果要删除所有匹配的元素,可以使用removeIf()方法。然后看看fastRemove是干什么的?可以发现他和remove(intindex)惊人的相似,没有区别。privatevoidfastRemove(intindex){modCount++;intnumMoved=大小-索引-1;如果(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=null;}publicEremove(intindex){rangeCheck(index);模数++;EoldValue=elementData(index);intnumMoved=大小-索引-1;如果(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=空;返回旧值;}不同之处可能在于rangeCheck和elementData(index)获取的是元素。你可以自己检查removeRange和removeAll。它实际上只是System.arraycopy。至于removeIf方法,我们下一节会详细讲到,还有fail-fast机制,下一节会简单讲到。看到这里,可以总结一下,如下图所示:##remove系列方法中的高亮方法:removeif()这一节,我们最后来看一下removeif()方法。里面其实有一个不错的idea,可以供大家借鉴。我们直接来看代码:publicbooleanremoveIf(Predicatefilter){Objects.requireNonNull(filter);//找出要删除的元素//在此阶段从过滤器谓词抛出的任何异常//将使集合保持不变intremoveCount=0;finalBitSetremoveSet=newBitSet(size);最终intexpectedModCount=modCount;finalintsize=this.size;对于(inti=0;modCount==expectedModCount&&i0;if(anyToRemove){finalintnewSize=size-removeCount;对于(inti=0,j=0;(i