HashMap和List遍歷方法及如何遍歷刪除元素總結(jié)
相信大家對(duì)集合遍歷再熟悉不過(guò)了,這里總結(jié)一下HashMap和List的遍歷方法,以及它們?cè)撊绾螌?shí)現(xiàn)遍歷刪除。
這里對(duì)于每種遍歷刪除出現(xiàn)的問(wèn)題的原因都給出了詳解!
(一)List的遍歷方法及如何實(shí)現(xiàn)遍歷刪除
我們?cè)煲粋€(gè)list出來(lái),接下來(lái)用不同方法遍歷刪除,如下代碼:
List<String> list= new ArrayList<String>(); famous.add("zs"); famous.add("ls"); famous.add("ww"); famous.add("dz");
1、for循環(huán)遍歷list:
for(int i=0;i<list.size();i++){ if(list.get(i).equals("ls")) list.remove(i); }
這是一種很常見(jiàn)的遍歷方式,但是使用這種遍歷刪除元素會(huì)出現(xiàn)問(wèn)題,原因在于刪除某個(gè)元素后,list的大小發(fā)生了變化,而你的索引也在變化,所以會(huì)導(dǎo)致你在遍歷的時(shí)候漏掉某些元素。比如當(dāng)你刪除第一個(gè)元素后,繼續(xù)根據(jù)索引訪問(wèn)第二個(gè)元素后,因?yàn)閯h除的原因,后面的元素都往前移動(dòng)了以為,所以實(shí)際訪問(wèn)的是第三個(gè)元素。因此,這種遍歷方式可以用在讀取元素,而不適合刪除元素。
2、增強(qiáng)for循環(huán):
for(String x:list){ if(x.equals("ls")) list.remove(x); }
這也是一種很常見(jiàn)的遍歷方式,但是使用這種遍歷刪除元素也會(huì)出現(xiàn)問(wèn)題,運(yùn)行時(shí)會(huì)報(bào)ConcurrentModificationException異常
其實(shí)增強(qiáng)for循環(huán)是java語(yǔ)法糖的一種體現(xiàn),如果大家通過(guò)反編譯得到字節(jié)碼,那么上面這段代碼的內(nèi)部實(shí)現(xiàn)如下所示:
for(Iterator<String> it = list.iterator();it.hasNext();){ String s = it.next(); if(s.equals("madehua")){ list.remove(s); } }
下面就解釋為什么會(huì)報(bào)ConcurrentModificationException異常。分析Iterator的源代碼,重點(diǎn)分析整個(gè)調(diào)用該過(guò)程中的
函數(shù)(hasNext和remove):
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; // size為集合中元素的個(gè)數(shù) } public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } /* 此方法并沒(méi)被調(diào)用,只是調(diào)用List.remove方法 public void remove() { checkForComodification(); try { ArrayList.this.remove(lastRet); // size字段減1 cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } */ final void checkForComodification() { // 檢查修改和當(dāng)前版本號(hào)是否一致,不一致則拋出異常 if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } // List.remove @Override public boolean remove(Object object) { Object[] a = array; int s = size; if (object != null) { for (int i = 0; i < s; i++) { if (object.equals(a[i])) { System.arraycopy(a, i + 1, a, i, --s - i); a[s] = null; // Prevent memory leak size = s; modCount++; // 核心代碼:修改了版本號(hào)。這樣當(dāng)checkForComodification的時(shí)候,modCount值就和expectedModCount不同 return true; } } } else { for (int i = 0; i < s; i++) { if (a[i] == null) { System.arraycopy(a, i + 1, a, i, --s - i); a[s] = null; // Prevent memory leak size = s; modCount++; return true; } } } return false; }
接下來(lái)梳理一下流程,這時(shí)候你就會(huì)發(fā)現(xiàn)這個(gè)異常是在next方法的checkForComodification中拋出的。拋出的原因是
modCount !=expectedModCount。這里的modCount是指這個(gè)list對(duì)象從呢我出來(lái)到現(xiàn)在被修改的次數(shù),當(dāng)調(diào)用list
的add或者remove方法的時(shí)候,這個(gè)modCount都會(huì)自動(dòng)增減;iterator創(chuàng)建的時(shí)候modCount被復(fù)制給了
expectedModcount,但是調(diào)用list的add和remove方法的時(shí)候不會(huì)同時(shí)自動(dòng)增減expectedModcount,這樣就導(dǎo)致
兩個(gè)count不相等,從而拋出異常。大家如果理解了上面的執(zhí)行流程,以后碰到類(lèi)似這種問(wèn)題,比如如果刪除的是倒數(shù)
第二個(gè)元素卻不會(huì)碰到異常。就會(huì)知道為什么了。
3、iterator遍歷刪除
Iterator<String> it = list.iterator(); while(it.hasNext()){ String x = it.next(); if(x.equals("del")){ it.remove(); } }
這種方式是可以正常遍歷和刪除的。但是你可能看到上面代碼感覺(jué)和增強(qiáng)for循環(huán)內(nèi)部實(shí)現(xiàn)的代碼差不多,其實(shí)差別就在于上面使用一個(gè)使用list.remove(),一個(gè)使用it.remove()。
(二)HashMap的遍歷刪除及如何實(shí)現(xiàn)遍歷刪除
一樣我們先造一個(gè)hashmap出來(lái),如下
private static HashMap<Integer, String> map = new HashMap<Integer, String>();; public static void main(String[] args) { for(int i = 0; i < 10; i++){ map.put(i, "value" + i); } }
1、第一種遍歷刪除:
for(Map.Entry<Integer, String> entry : map.entrySet()){ Integer key = entry.getKey(); if(key % 2 == 0){ System.out.println("To delete key " + key); map.remove(key); System.out.println("The key " + + key + " was deleted"); }
這種遍歷刪除依舊會(huì)報(bào)ConcurrentModificationException異常,
2、第二種遍歷刪除:
Set<Integer> keySet = map.keySet(); for(Integer key : keySet){ if(key % 2 == 0){ System.out.println("To delete key " + key); keySet.remove(key); System.out.println("The key " + + key + " was deleted"); } }
這種遍歷刪除依舊會(huì)報(bào)ConcurrentModificationException異常,
3、第三種遍歷刪除:
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while(it.hasNext()){ Map.Entry<Integer, String> entry = it.next(); Integer key = entry.getKey(); if(key % 2 == 0){ System.out.println("To delete key " + key); it.remove(); System.out.println("The key " + + key + " was deleted"); } }
這種遍歷是OK的
分析上述原因,如果大家理解了List的遍歷刪除,那么感覺(jué)HashMap的遍歷刪除是不是有類(lèi)似之處啊。下面就分析一下原因:
如果查詢(xún)?cè)创a以上的三種的刪除方式都是通過(guò)調(diào)用HashMap.removeEntryForKey方法來(lái)實(shí)現(xiàn)刪除key的操作。
在removeEntryForKey方法內(nèi)知識(shí)一場(chǎng)了key modCount就會(huì)執(zhí)行一次自增操作,此時(shí)modCount就與expectedModCOunt不一致了
,上面三種remove實(shí)現(xiàn)中,只有第三種iterator的remove方法在調(diào)用完removeEntryForKey方法后同步了expectedModCount值與
modCount相同,所以iterator方式不會(huì)拋出異常。最后希望大家遇到問(wèn)題到查詢(xún)?cè)创a,它會(huì)給你最好的解釋?zhuān)?br />
---------------------
作者:demohui
來(lái)源:CSDN
原文:https://blog.csdn.net/demohui/article/details/77748809
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
相關(guān)文章
Spring實(shí)戰(zhàn)之使用Resource作為屬性操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之使用Resource作為屬性,結(jié)合實(shí)例形式分析了spring載人Resource作為屬性相關(guān)配置與使用技巧,需要的朋友可以參考下2020-01-01Idea 同一窗口導(dǎo)入多個(gè)項(xiàng)目的實(shí)現(xiàn)步驟
本文主要介紹了Idea 同一窗口導(dǎo)入多個(gè)項(xiàng)目的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Java微服務(wù)Nacos Config配置中心超詳細(xì)講解
配置文件相對(duì)分散。在一個(gè)微服務(wù)架構(gòu)下,配置文件會(huì)隨著微服務(wù)的增多變的越來(lái)越多,而且分散 在各個(gè)微服務(wù)中,不好統(tǒng)一配置和管理。每一個(gè)環(huán)境所使用的配置理論上都是不同的,一旦需要修改,就需要我們?nèi)ジ鱾€(gè)微服務(wù)下手動(dòng)維護(hù)2023-02-02java基于JDBC連接Oracle 11g Release2實(shí)例分析
這篇文章主要介紹了java基于JDBC連接Oracle 11g Release2的方法,實(shí)例分析了JDBC連接Oracle 11g Release2容易出現(xiàn)的異常與解決方法,需要的朋友可以參考下2015-06-06java 查詢(xún)oracle數(shù)據(jù)庫(kù)所有表DatabaseMetaData的用法(詳解)
下面小編就為大家?guī)?lái)一篇java 查詢(xún)oracle數(shù)據(jù)庫(kù)所有表DatabaseMetaData的用法(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11java中JDBC實(shí)現(xiàn)往MySQL插入百萬(wàn)級(jí)數(shù)據(jù)的實(shí)例代碼
這篇文章主要介紹了java中JDBC實(shí)現(xiàn)往MySQL插入百萬(wàn)級(jí)數(shù)據(jù)的實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-01-01jstl之map,list訪問(wèn)遍歷以及el表達(dá)式map取值的實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇jstl之map,list訪問(wèn)遍歷以及el表達(dá)式map取值的實(shí)現(xiàn)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03mybatis-generator-gui 工具使用(圖形化工具)
基于 mybatis generator 開(kāi)發(fā)一款界面工具, 本工具可以使你非常容易及快速生成 Mybatis 的 Java POJO 文件及數(shù)據(jù)庫(kù) Mapping 文件。本文重點(diǎn)給大家介紹mybatis-generator-gui 工具使用,感興趣的朋友一起看看吧2022-03-03