如何在Java中处理ConcurrentModificationException? 在循环中从ArrayList中删除元素时要当心...
從Java中從ArrayList中刪除元素時常見的問題之一是ConcurrentModificationException。 如果您對索引使用經典的for循環或增強的for循環,并嘗試使用remove()方法從ArrayList中remove()元素,則將獲得C oncurrentModificationException但如果使用Iterator的remove方法或ListIterator的
remove()方法,那么您將不會遇到此錯誤,并且能夠刪除該元素。 這是Java中的一條不成文的規則,在遍歷列表時,在集合支持故障安全迭代器(例如CopyOnWriteArrayList remove()之前,不應該add()或remove()元素,該迭代器對列表的副本而不是原始列表進行操作。
出現此錯誤的主要問題是,它使開發人員混淆了列表正在被多個線程修改,這就是Java拋出此錯誤的原因,這不是事實。 大多數時候
即使沒有多個線程修改列表,也會出現ConcurrentModificationException 。
這是用詞不當,請不要為此而上當。 盡管似乎自然而然地認為其他線程可能試圖同時修改集合,但這通常違反了Java規則。
在本文中,我將解釋此錯誤,并且我們將提供許多代碼示例,即使使用單線程也可以重現此代碼,并了解如何在Java中修改ArrayList時如何避免并發修改錯誤。
順便說一句,如果您不熟悉集合類(例如ArrayList本身),那么您應該參加在線課程,例如
Java基礎知識:學習在Udemy上編寫正確的代碼是一個不錯的起點。
單線程中的ConcurrentModificationException
這是在Java中再現并發修改異常的第一個示例。 在此程序中,我們使用增強的foreach循環遍歷ArrayList并刪除選擇性元素,例如,使用ArrayList的remove方法刪除匹配特定條件的元素。
例如,在下面的代碼中,我們首先添加了幾本不錯的編程書,例如, Programming Pearls , Clean Code , Code Complete到ArrayList中,然后刪除標題中帶有Code的任何元素。
package beginner;import java.util.ArrayList; import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>(); listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");// Using forEach loop to iterate and removing // element during iteration will throw // ConcurrentModificationException in Javafor(String book: listOfBooks){if(book.contains("Code"));{listOfBooks.remove(book);}}}} Output Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(Unknown Source)at java.util.ArrayList$Itr.next(Unknown Source)at beginner.HelloWorldApp.main(HelloWorldApp.java:18)您可以看到即使我們只有一個線程,即與ArrayList一起運行的主線程,也會出現此錯誤。 之所以會出現ConcurrentModification錯誤,是因為我們沒有使用Iterator,而是僅調用listOfBooks.remove()方法。
在這段代碼中,我使用的Java 1.5增強的for循環,你必須知道如何for循環增強 Java中的作品。
for循環和增強的for循環之間的區別在于,以后內部使用Iterator遍歷集合的所有元素。 有關更深入的討論,請參見此處
使用Classical for循環和ArrayList.remove(index)
這是從ArrayList中刪除元素的另一個有趣的代碼示例。 令人驚訝的是,當您第一次運行此代碼時,它不會引發ConcurrentModificationException嗎? 你知道為什么嗎?
好吧,在查看代碼后的解釋之前,請嘗試一下。 確實是有關Java編程語言和Collection框架的此類次要細節,這將使您成為一名優秀的開發人員,并且如果您正在為此做好準備,還將幫助您獲得Java認證 。
package beginner;import java.util.ArrayList; import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>(); listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");System.out.println("List before : " + listOfBooks);for(int i=0; i<listOfBooks.size(); i++){String book = listOfBooks.get(i);if(book.contains("Programming")){System.out.println("Removing " + book);listOfBooks.remove(i); // will throw CME}}System.out.println("List after : " + listOfBooks);}}Output List before : [Programming Pearls, Clean Code, Effective Java, Code Complete] Removing Programming Pearls List after : [Clean Code, Effective Java, Code Complete]這段代碼不會引發ConcurrentModificationException因為在這里我們沒有使用 Iterator,而是在使用傳統的for循環。
是Iterator引發ConcurrentModificationException ,而不是ArrayList的remove方法,因此在下面的代碼中看不到該錯誤。
如果查看ArrayList.java的代碼,您會注意到有一個實現Iterator接口的嵌套類,并且next()方法調用checkForComodification()函數,該函數實際上檢查ArrayList在迭代過程中是否已修改,如果modCount沒有, “T配以expectedModCount則拋出ConcurrentModificationException 。
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException(); }此類問題在Oracle Java認證中也很受歡迎,例如OCAJP( 1z0-808 )和OCPJP( 1Z0-809 ),因此,如果您準備參加這些考試,則應該知道答案。
這是ArrayList.java類的完整代碼片段,供您快速參考:
使用迭代器,但使用ArrayList的remove方法
現在,讓我們看另一個代碼示例,其中Java程序員認為他已正確完成了所有工作,但仍收到并發修改異常。 您能發現錯誤嗎? 這確實很常見,我在Java論壇,StackOverflow和要求解決此問題的Facebook Java組上經常看到這種代碼。
package beginner;import java.util.ArrayList; import java.util.Iterator; import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>(); listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");Iterator<String> iterator = listOfBooks.iterator();while(iterator.hasNext()){String book = iterator.next();listOfBooks.remove(book);}}}Output Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(Unknown Source)at java.util.ArrayList$Itr.next(Unknown Source)at beginner.HelloWorldApp.main(HelloWorldApp.java:18)這段代碼的真正問題在于,即使代碼使用Iterator遍歷ArrayList ,也并不是真正使用Iterator.remove()方法刪除該元素。 它只是使用Iterator獲取下一個元素,但調用ArrayList.remove()方法刪除該元素。
我知道,當您知道原因時就很容易了,但實際上,很多時候程序員甚至要花幾個小時才能弄清楚問題出在哪里。 因此,請當心。
順便說一句,如果您正在學習Java,那么我建議加入Complete Java Masterclass,以更好地學習Java并避免此類常見錯誤。
刪除元素的正確方法是使用Iterator的remove方法
最后,這是在迭代過程中從ArrayList刪除元素的正確方法。 在此示例中,我們使用Iterator進行了迭代以及刪除了元素。 該代碼還可以,但是有一個嚴重的限制,您只能使用此代碼刪除當前元素。 您不能從Java中的ArrayList中刪除任何任意元素。
package beginner;import java.util.ArrayList; import java.util.Iterator; import java.util.List;public class HelloWorldApp{public static void main(String... args){List<String> listOfBooks = new ArrayList<>(); listOfBooks.add("Programming Pearls");listOfBooks.add("Clean Code");listOfBooks.add("Effective Java");listOfBooks.add("Code Complete");System.out.println("List before : " + listOfBooks);Iterator<String> iterator = listOfBooks.iterator();while(iterator.hasNext()){String book = iterator.next();System.out.println("Removing " + book);iterator.remove();}System.out.println("List after : " + listOfBooks);}} Output List before : [Programming Pearls, Clean Code, Effective Java, Code Complete] Removing Programming Pearls Removing Clean Code Removing Effective Java Removing Code Complete List after : []同樣的行為也適用于ListIterator 。 我的意思是您可以用ListIterator替換Iterator,并且代碼可以正常工作。 ListIterator還允許您在兩個方向上進行導航,即向前和向后導航。
這就是在迭代過程中如何從ArrayList中刪除元素時如何避免ConcurrentModificationException的全部內容。 您可以使用相同的技術來避免ConcurrentModificationException同時從具有快速失敗的Iterator的任何其他集合類中刪除元素,例如LinkedList。 順便說一句,如果您不熟悉Java編程,那么可以參加一門很好的綜合課程,例如Java Basics:學習在Udemy上編寫正確的方法 ,可以幫助您更好更快地學習Java。
您可能喜歡的其他Java故障排除指南
如何在Java中解決ArrayIndexOutOfBoundsException? ( 指南 )
如何在Java中解決NullPointerException? (指南)
如何解決“系統找不到指定的路徑”錯誤? ( 解決方案 ) 從命令行運行Java程序時如何解決NoClassDefFoundError? ( 解決方案 ) 如何解決Android Studio中的“找不到JVM,請安裝64位JDK”錯誤? ( 解決方案 ) 如何處理JDBC和MySQL中SQLException“找不到合適的驅動程序”錯誤? ( 指南 ) 如何解決Java中的NumberFormatException? ( 指南 ) 如何解決Minecraft – java.lang.UnsatisfiedLinkError:lwjgl64.dll:訪問被拒絕? ( 解決方案 ) 如何在Java中修復java.lang.ArrayIndexOutOfBoundsException:1? ( 解決方案 ) 如何修復java.net.SocketException:軟件導致連接中止:recv失敗( 修復 )
感謝您閱讀本教程,如果您喜歡本教程,請與您的朋友和同事分享。 如果您有任何問題或建議,請發表評論。
翻譯自: https://www.javacodegeeks.com/2018/01/deal-concurrentmodificationexception-java-beware-removing-elements-arraylist-loop.html
總結
以上是生活随笔為你收集整理的如何在Java中处理ConcurrentModificationException? 在循环中从ArrayList中删除元素时要当心...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 非对称加密 公钥私钥_选择Java加密算
- 下一篇: 网络研讨室_网络研讨会:Java 9的第