HashMap和List遍历方法及如何遍历删除元素总结
相信大家对集合遍历再熟悉不过了,这里总结一下HashMap和List的遍历方法,以及它们该如何实现遍历删除。
这里对于每种遍历删除出现的问题的原因都给出了详解!
(一)List的遍历方法及如何实现遍历删除
我们造一个list出来,接下来用不同方法遍历删除,如下代码:
Listlist=newArrayList (); famous.add("zs"); famous.add("ls"); famous.add("ww"); famous.add("dz");
1、for循环遍历list:
for(inti=0;i这是一种很常见的遍历方式,但是使用这种遍历删除元素会出现问题,原因在于删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第一个元素后,继续根据索引访问第二个元素后,因为删除的原因,后面的元素都往前移动了以为,所以实际访问的是第三个元素。因此,这种遍历方式可以用在读取元素,而不适合删除元素。
2、增强for循环:
for(Stringx:list){ if(x.equals("ls")) list.remove(x); }这也是一种很常见的遍历方式,但是使用这种遍历删除元素也会出现问题,运行时会报ConcurrentModificationException异常
其实增强for循环是java语法糖的一种体现,如果大家通过反编译得到字节码,那么上面这段代码的内部实现如下所示:for(Iteratorit=list.iterator();it.hasNext();){ Strings=it.next(); if(s.equals("madehua")){ list.remove(s); } } 下面就解释为什么会报ConcurrentModificationException异常。分析Iterator的源代码,重点分析整个调用该过程中的
函数(hasNext和remove):privateclassItrimplementsIterator{ intcursor;//indexofnextelementtoreturn intlastRet=-1;//indexoflastelementreturned;-1ifnosuch intexpectedModCount=modCount; publicbooleanhasNext(){ returncursor!=size;//size为集合中元素的个数 } publicEnext(){ checkForComodification(); inti=cursor; if(i>=size) thrownewNoSuchElementException(); Object[]elementData=ArrayList.this.elementData; if(i>=elementData.length) thrownewConcurrentModificationException(); cursor=i+1; return(E)elementData[lastRet=i]; } /*此方法并没被调用,只是调用List.remove方法 publicvoidremove(){ checkForComodification(); try{ ArrayList.this.remove(lastRet); //size字段减1 cursor=lastRet; lastRet=-1; expectedModCount=modCount; }catch(IndexOutOfBoundsExceptionex){ thrownewConcurrentModificationException(); } } */ finalvoidcheckForComodification(){ //检查修改和当前版本号是否一致,不一致则抛出异常 if(modCount!=expectedModCount) thrownewConcurrentModificationException(); } } //List.remove @Overridepublicbooleanremove(Objectobject){ Object[]a=array; ints=size; if(object!=null){ for(inti=0;i 接下来梳理一下流程,这时候你就会发现这个异常是在next方法的checkForComodification中抛出的。抛出的原因是
modCount!=expectedModCount。这里的modCount是指这个list对象从呢我出来到现在被修改的次数,当调用list
的add或者remove方法的时候,这个modCount都会自动增减;iterator创建的时候modCount被复制给了
expectedModcount,但是调用list的add和remove方法的时候不会同时自动增减expectedModcount,这样就导致
两个count不相等,从而抛出异常。大家如果理解了上面的执行流程,以后碰到类似这种问题,比如如果删除的是倒数
第二个元素却不会碰到异常。就会知道为什么了。
3、iterator遍历删除
Iteratorit=list.iterator(); while(it.hasNext()){ Stringx=it.next(); if(x.equals("del")){ it.remove(); } } 这种方式是可以正常遍历和删除的。但是你可能看到上面代码感觉和增强for循环内部实现的代码差不多,其实差别就在于上面使用一个使用list.remove(),一个使用it.remove()。
(二)HashMap的遍历删除及如何实现遍历删除
一样我们先造一个hashmap出来,如下
privatestaticHashMapmap=newHashMap ();; publicstaticvoidmain(String[]args){ for(inti=0;i<10;i++){ map.put(i,"value"+i); } } 1、第一种遍历删除:
for(Map.Entryentry:map.entrySet()){ Integerkey=entry.getKey(); if(key%2==0){ System.out.println("Todeletekey"+key); map.remove(key); System.out.println("Thekey"++key+"wasdeleted"); } 这种遍历删除依旧会报ConcurrentModificationException异常,
2、第二种遍历删除:
SetkeySet=map.keySet(); for(Integerkey:keySet){ if(key%2==0){ System.out.println("Todeletekey"+key); keySet.remove(key); System.out.println("Thekey"++key+"wasdeleted"); } } 这种遍历删除依旧会报ConcurrentModificationException异常,
3、第三种遍历删除:
Iterator>it=map.entrySet().iterator(); while(it.hasNext()){ Map.Entry entry=it.next(); Integerkey=entry.getKey(); if(key%2==0){ System.out.println("Todeletekey"+key); it.remove(); System.out.println("Thekey"++key+"wasdeleted"); } } 这种遍历是OK的
分析上述原因,如果大家理解了List的遍历删除,那么感觉HashMap的遍历删除是不是有类似之处啊。下面就分析一下原因:
如果查询源代码以上的三种的删除方式都是通过调用HashMap.removeEntryForKey方法来实现删除key的操作。
在removeEntryForKey方法内知识一场了key modCount就会执行一次自增操作,此时modCount就与expectedModCOunt不一致了
,上面三种remove实现中,只有第三种iterator的remove方法在调用完removeEntryForKey方法后同步了expectedModCount值与
modCount相同,所以iterator方式不会抛出异常。最后希望大家遇到问题到查询源代码,它会给你最好的解释!
---------------------
作者:demohui
来源:CSDN
原文:https://blog.csdn.net/demohui/article/details/77748809
版权声明:本文为博主原创文章,转载请附上博文链接!