fail-fast机制(遍历的同时删除List中的对象)
生活随笔
收集整理的這篇文章主要介紹了
fail-fast机制(遍历的同时删除List中的对象)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
遍歷刪除List中的元素有很多種方法,當(dāng)運(yùn)用不當(dāng)?shù)臅r(shí)候就會(huì)產(chǎn)生問(wèn)題。下面主要看看以下幾種遍歷刪除List中元素的形式:
/**?? ?*?使用增強(qiáng)的for循環(huán)?? ?*?在循環(huán)過(guò)程中從List中刪除非基本數(shù)據(jù)類(lèi)型以后,繼續(xù)循環(huán)List時(shí)會(huì)報(bào)ConcurrentModificationException?? ?*/???? public?void?listRemove()?{???? ????List<Student>?students?=?this.getStudents();???? ????for?(Student?stu?:?students)?{???? ????????if?(stu.getId()?==?2)????? ????????????students.remove(stu);???? ????}???? }????
[java] view plaincopy /**?? ?*?像這種使用增強(qiáng)的for循環(huán)對(duì)List進(jìn)行遍歷刪除,但刪除之后馬上就跳出的也不會(huì)出現(xiàn)異常?? ?*/???? public?void?listRemoveBreak()?{???? ????List<Student>?students?=?this.getStudents();???? ????for?(Student?stu?:?students)?{???? ????????if?(stu.getId()?==?2)?{???? ????????????students.remove(stu);???? ????????????break;???? ????????}???? ????}???? }????
[java] view plaincopy /**?? ?*?這種不使用增強(qiáng)的for循環(huán)的也可以正常刪除和遍歷,?? ?*?這里所謂的正常是指它不會(huì)報(bào)異常,但是刪除后得到的?? ?*?數(shù)據(jù)不一定是正確的,這主要是因?yàn)閯h除元素后,被刪除元素后?? ?*?的元素索引發(fā)生了變化。假設(shè)被遍歷list中共有10個(gè)元素,當(dāng)?? ?*?刪除了第3個(gè)元素后,第4個(gè)元素就變成了第3個(gè)元素了,第5個(gè)就變成?? ?*?了第4個(gè)了,但是程序下一步循環(huán)到的索引是第4個(gè),?? ?*?這時(shí)候取到的就是原本的第5個(gè)元素了。?? ?*/???? public?void?listRemove2()?{???? ????List<Student>?students?=?this.getStudents();???? ????for?(int?i=0;?i<students.size();?i++)?{???? ????????if?(students.get(i).getId()%3?==?0)?{???? ????????????Student?student?=?students.get(i);???? ????????????students.remove(student);???? ????????}???? ????}???? }????
[java] view plaincopy/**?? ?*?使用Iterator的方式可以順利刪除和遍歷?? ?*/???? public?void?iteratorRemove()?{???? ????List<Student>?students?=?this.getStudents();???? ????System.out.println(students);???? ????Iterator<Student>?stuIter?=?students.iterator();???? ????while?(stuIter.hasNext())?{???? ????????Student?student?=?stuIter.next();???? ????????if?(student.getId()?%?2?==?0)???? ????????????stuIter.remove();//這里要使用Iterator的remove方法移除當(dāng)前對(duì)象,如果使用List的remove方法,則同樣會(huì)出現(xiàn)ConcurrentModificationException???? ????}???? ????System.out.println(students);???? }????
?? 當(dāng)使用 fail-fast iterator 對(duì) Collection 或 Map進(jìn)行迭代操作過(guò)程中嘗試直接修改 Collection / Map 的內(nèi)容時(shí),即使是在單線(xiàn)程下運(yùn)行,java.util.ConcurrentModificationException 異常也將被拋出。
Iterator是工作在一個(gè)獨(dú)立的線(xiàn)程中,并且擁有一個(gè) mutex 鎖。 Iterator被創(chuàng)建之后會(huì)建立一個(gè)指向原來(lái)對(duì)象的單鏈索引表,當(dāng)原來(lái)的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出java.util.ConcurrentModificationException 異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。但你可以使用 Iterator 本身的方法 remove() 來(lái)刪除對(duì)象, Iterator.remove() 方法會(huì)在刪除當(dāng)前迭代對(duì)象的同時(shí)維護(hù)索引的一致性。
有意思的是如果你的 Collection / Map 對(duì)象實(shí)際只有一個(gè)元素的時(shí)候,ConcurrentModificationException 異常并不會(huì)被拋出。這也就是為什么在 javadoc 里面指出: itwould be wrong to write a program that depended on this exception forits correctness: ConcurrentModificationException should be used only todetect bugs.
附:來(lái)自ibm developerworks上對(duì)java.util.concurrent包的說(shuō)明片段:
?????java.util 包中的集合類(lèi)都返回 fail-fast 迭代器,這意味著它們假設(shè)線(xiàn)程在集合內(nèi)容中進(jìn)行迭代時(shí),集合不會(huì)更改它的內(nèi)容。如果fail-fast 迭代器檢測(cè)到在迭代過(guò)程中進(jìn)行了更改操作,那么它會(huì)拋出 ConcurrentModificationException,這是不可控異常。
????? 在迭代過(guò)程中不更改集合的要求通常會(huì)對(duì)許多并發(fā)應(yīng)用程序造成不便。相反,比較好的是它允許并發(fā)修改并確保迭代器只要進(jìn)行合理操作,就可以提供集合的一致視圖,如 java.util.concurrent 集合類(lèi)中的迭代器所做的那樣。
????java.util.concurrent 集合返回的迭代器稱(chēng)為弱一致的(weakly consistent)迭代器。對(duì)于這些類(lèi),如果元素自從迭代開(kāi)始已經(jīng)刪除,且尚未由 next()方法返回,那么它將不返回到調(diào)用者。如果元素自迭代開(kāi)始已經(jīng)添加,那么它可能返回調(diào)用者,也可能不返回。在一次迭代中,無(wú)論如何更改底層集合,元素不會(huì)被返回兩次。
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!
1.通過(guò)增強(qiáng)的for循環(huán)刪除符合條件的多個(gè)元素
2.通過(guò)增強(qiáng)的for循環(huán)刪除符合條件的一個(gè)元素
3.通過(guò)普通的for刪除刪除符合條件的多個(gè)元素
4.通過(guò)Iterator進(jìn)行遍歷刪除符合條件的多個(gè)元素
?
[java] view plaincopy[java] view plaincopy
那么看到以上的幾段代碼之后,我們來(lái)分析一下他的原因,分析原因之前我們先來(lái)認(rèn)識(shí)一個(gè)詞fail-fast
百度百科是這么說(shuō)的:
fail-fast 機(jī)制是java集合(Collection)中的一種錯(cuò)誤機(jī)制。當(dāng)多個(gè)線(xiàn)程對(duì)同一個(gè)集合的內(nèi)容進(jìn)行操作時(shí),就可能會(huì)產(chǎn)生fail-fast事件。例如:當(dāng)某一個(gè)線(xiàn)程A通過(guò)iterator去遍歷某集合的過(guò)程中,若該集合的內(nèi)容被其他線(xiàn)程所改變了;那么線(xiàn)程A訪(fǎng)問(wèn)集合時(shí),就會(huì)拋出ConcurrentModificationException異常,產(chǎn)生fail-fast事件。 要了解fail-fast機(jī)制,我們首先要對(duì)ConcurrentModificationException 異常有所了解。當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但不允許這種修改時(shí)就拋出該異常。同時(shí)需要注意的是,該異常不會(huì)始終指出對(duì)象已經(jīng)由不同線(xiàn)程并發(fā)修改,如果單線(xiàn)程違反了規(guī)則,同樣也有可能會(huì)拋出改異常。 誠(chéng)然,迭代器的快速失敗行為無(wú)法得到保證,它不能保證一定會(huì)出現(xiàn)該錯(cuò)誤,但是快速失敗操作會(huì)盡最大努力拋出ConcurrentModificationException異常,所以因此,為提高此類(lèi)操作的正確性而編寫(xiě)一個(gè)依賴(lài)于此異常的程序是錯(cuò)誤的做法,正確做法是:ConcurrentModificationException 應(yīng)該僅用于檢測(cè) bug。?? 當(dāng)使用 fail-fast iterator 對(duì) Collection 或 Map進(jìn)行迭代操作過(guò)程中嘗試直接修改 Collection / Map 的內(nèi)容時(shí),即使是在單線(xiàn)程下運(yùn)行,java.util.ConcurrentModificationException 異常也將被拋出。
Iterator是工作在一個(gè)獨(dú)立的線(xiàn)程中,并且擁有一個(gè) mutex 鎖。 Iterator被創(chuàng)建之后會(huì)建立一個(gè)指向原來(lái)對(duì)象的單鏈索引表,當(dāng)原來(lái)的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出java.util.ConcurrentModificationException 異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。但你可以使用 Iterator 本身的方法 remove() 來(lái)刪除對(duì)象, Iterator.remove() 方法會(huì)在刪除當(dāng)前迭代對(duì)象的同時(shí)維護(hù)索引的一致性。
有意思的是如果你的 Collection / Map 對(duì)象實(shí)際只有一個(gè)元素的時(shí)候,ConcurrentModificationException 異常并不會(huì)被拋出。這也就是為什么在 javadoc 里面指出: itwould be wrong to write a program that depended on this exception forits correctness: ConcurrentModificationException should be used only todetect bugs.
附:來(lái)自ibm developerworks上對(duì)java.util.concurrent包的說(shuō)明片段:
?????java.util 包中的集合類(lèi)都返回 fail-fast 迭代器,這意味著它們假設(shè)線(xiàn)程在集合內(nèi)容中進(jìn)行迭代時(shí),集合不會(huì)更改它的內(nèi)容。如果fail-fast 迭代器檢測(cè)到在迭代過(guò)程中進(jìn)行了更改操作,那么它會(huì)拋出 ConcurrentModificationException,這是不可控異常。
????? 在迭代過(guò)程中不更改集合的要求通常會(huì)對(duì)許多并發(fā)應(yīng)用程序造成不便。相反,比較好的是它允許并發(fā)修改并確保迭代器只要進(jìn)行合理操作,就可以提供集合的一致視圖,如 java.util.concurrent 集合類(lèi)中的迭代器所做的那樣。
????java.util.concurrent 集合返回的迭代器稱(chēng)為弱一致的(weakly consistent)迭代器。對(duì)于這些類(lèi),如果元素自從迭代開(kāi)始已經(jīng)刪除,且尚未由 next()方法返回,那么它將不返回到調(diào)用者。如果元素自迭代開(kāi)始已經(jīng)添加,那么它可能返回調(diào)用者,也可能不返回。在一次迭代中,無(wú)論如何更改底層集合,元素不會(huì)被返回兩次。
新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!
總結(jié)
以上是生活随笔為你收集整理的fail-fast机制(遍历的同时删除List中的对象)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: NYOJ 330 一个简单的数学题
- 下一篇: 不就是SELECT COUNT语句吗,竟