宣武富阳网站建设,南昌公众号开发公司,广西住房和城乡建设厅网上办事,广州免费建站哪里有前些天发现了一个巨牛的人工智能学习网站#xff0c;通俗易懂#xff0c;风趣幽默#xff0c;忍不住分享一下给大家。点击跳转到教程。
Java在操作ArrayList、HashMap、TreeMap等容器类时#xff0c;遇到了java.util.ConcurrentModificationException异常。以ArrayList为例…前些天发现了一个巨牛的人工智能学习网站通俗易懂风趣幽默忍不住分享一下给大家。点击跳转到教程。
Java在操作ArrayList、HashMap、TreeMap等容器类时遇到了java.util.ConcurrentModificationException异常。以ArrayList为例如下面的代码片段 [java] view plain copy import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class Test { public static void main(String[] args){ ListString strList new ArrayListString(); strList.add(string1); strList.add(string2); strList.add(string3); strList.add(string4); strList.add(string5); strList.add(string6); // 操作方式1whileIterator报错 IteratorString it strList.iterator(); while(it.hasNext()) { String s it.next(); if(string2.equals(s)) { strList.remove(s); } } // 解决方案1使用Iterator的remove方法删除元素 // 操作方式1whileIterator不报错 // IteratorString it strList.iterator(); // while(it.hasNext()) { // String s it.next(); // if(string2.equals(s)) { // it.remove(); // } // } // 操作方式2foreachIterator报错 // for(String s : strList) { // if(string2.equals(s)) { // strList.remove(s); // } // } // 解决方案2不使用Iterator遍历注意索引的一致性 // 操作方式3for非Iterator不报错注意修改索引 // for(int i0; istrList.size(); i) { // String s strList.get(i); // if(string2.equals(s)) { // strList.remove(s); // strList.remove(i); // i--;// 元素位置发生变化修改i // } // } // 解决方案3新建一个临时列表暂存要删除的元素最后一起删除 // ListString templist new ArrayListString(); // for (String s : strList) { // if(s.equals(string2)) { // templist.add(s); // } // } // // 查看removeAll源码其使用Iterator进行遍历 // strList.removeAll(templist); // 解决方案4使用线程安全CopyOnWriteArrayList进行删除操作 // ListString strList new CopyOnWriteArrayListString(); // strList.add(string1); // strList.add(string2); // strList.add(string3); // strList.add(string4); // strList.add(string5); // strList.add(string6); // IteratorString it strList.iterator(); // while (it.hasNext()) { // String s it.next(); // if (s.equals(string2)) { // strList.remove(s); // } // } } } 执行上述代码后报错如下 [plain] view plain copy Exception in thread main java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(Unknown Source) at java.util.ArrayList$Itr.next(Unknown Source) at concurrentModificationException.Test.main(Test.java:21) 在第21行报错即it.next()迭代器在获取下一个元素时报错。找到java.util.ArrayList第830行看到it.next()的源代码如下 [java] view plain copy SuppressWarnings(unchecked) 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]; } 调用了checkForComodification()方法代码如下 [java] view plain copy final void checkForComodification() { if (modCount ! expectedModCount) throw new ConcurrentModificationException(); } 即比较modCount和expectedModCount两个是否相等。modCount是ArrayList从AbstractList继承来的属性查看modCount属性的doc文档可知modCount表示列表list被结构性修改structurally modified的次数。structurally modified是指造成列表中元素个数发生变化的操作ArrayList中的add,addAll,remove, fastRemove,clear, removeRange, ensureCapacity, ensureCapacityInternal, ensureExplicitCapacity等方法都会使modCount加1而batchRemove,removeAll,retainAll等方法则根据删除的元素数增加modCount的值removeAll和retainAll都是调用batchRemove实现具体modCount的修改算法还需研究。 第一个代码片段中的操作方式1和操作方式2都是采用了迭代器的方式。当使用iterator方法得到Iterator对象(或者使用listIterator获得ListItr对象)其实是返回了一个Iterator接口的实现类ArrayList$Itr继承自AbstractList$Itr该类为ArrayList的一内部类该类中有一个expectedModCount字段当调用ArrayList$Itr的next方法时会先检查modCount的值是否等于expectedModCount的值其实在调用next, remove, previous, set, add等方法时都会检查不相等时就会抛出java.util.ConcurrentModificationException异常。这种现象在Java doc中称作fail-fast。
为什么会抛出该异常呢从代码可以看到调用了6次add方法这时modCount的值也就为6当当使用iterator方法得到Iterator对象时把modCount的值赋给了expectedModCount开始时expectedModCount与modCount是相等的当迭代到第二个元素(index1)“string2”时因为if条件为true于是又调用了remove方法调用remove方法时modCount值又加1此时modCount的值为7了而expectedModCount的值并没有改变当再次调用ArrayList$Itr的next方法时检测到modeCount与expectedModCount不相等了于是抛出异常。
当把if语句写成if(s.equals(string5))时又没有抛出该异常这又是为什么呢ArrayList$Itr中还有一个名为cursor的字段用来指向迭代时要操作的元素索引初始值为0每调用一次next方法该字段值加1注意是先从集合中取出了元素再加1的。当判断string5时注意是倒数第二个元素这些cursor的值为5移除掉元素string5时List的size为5当调用ArrayList$Itr的hasNext方法判断有无下一个元素时判断的依据为cursor的值与size是否相等不相等则还有下一个元素而此时两者值刚好相等也就没有往下执行next方法了也就没有抛出异常因此删掉倒数第二个元素时不会抛异常的异常。
解决方案有四种直接看第一段代码即可。 参考链接
http://blog.csdn.NET/xtayfjpk/article/details/8451178
《多线程情况下只能使用CopyOnWriteArrayList》http://www.2cto.com/kf/201403/286536.html