android java.util.ConcurrentModificationException
android java.util.ConcurrentModificationException
1. 使用 java.util.concurrent 包下的集合
java.util.concurrent.ConcurrentHashMap;
java.util.concurrent.CopyOnWriteArrayList;
Please refer tohttp://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html
2. 循环 list or map 时 使用 iterator 来删除被循环的元素,或者将要删除或者增加的 元素先保存在一个临时集合里,待循环结束后再一次性操作,参考:http://zhoujianghai.iteye.com/blog/1041555
以下内容转发自: http://zhoujianghai.iteye.com/blog/1041555
用iterator遍历集合时碰到java.util.ConcurrentModificationException这个异常,
下面以List为例来解释为什么会报java.util.ConcurrentModificationException这个异常,代码如下:
Java代码- publicstaticvoidmain(String[]args){
- List<String>list=newArrayList<String>();
- list.add("1");
- list.add("2");
- list.add("3");
- list.add("4");
- list.add("5");
- list.add("6");
- list.add("7");
- List<String>del=newArrayList<String>();
- del.add("5");
- del.add("6");
- del.add("7");
- <spanstyle="color:#ff0000;">for(Stringstr:list){
- if(del.contains(str)){
- list.remove(str);
- }
- }</span>
- }
运行这段代码会出现如下异常:
Java代码- Exceptioninthread"main"java.util.ConcurrentModificationException
for(String str : list) 这句话实际上是用到了集合的iterator()方法
JDK java.util.AbstractList类中相关源码
Java代码- publicIterator<E>iterator(){
- returnnewItr();
- }
java.util.AbstractList的内部类Itr的源码如下:
Java代码- privateclassItrimplementsIterator<E>{
- /**
- *Indexofelementtobereturnedbysubsequentcalltonext.
- */
- intcursor=0;
- /**
- *Indexofelementreturnedbymostrecentcalltonextor
- *previous.Resetto-1ifthiselementisdeletedbyacall
- *toremove.
- */
- intlastRet=-1;
- /**
- *ThemodCountvaluethattheiteratorbelievesthatthebacking
- *Listshouldhave.Ifthisexpectationisviolated,theiterator
- *hasdetectedconcurrentmodification.
- */
- intexpectedModCount=modCount;
- publicbooleanhasNext(){
- returncursor!=size();
- }
- publicEnext(){
- checkForComodification();//检测modCount和expectedModCount的值!!
- try{
- Enext=get(cursor);
- lastRet=cursor++;
- returnnext;
- }catch(IndexOutOfBoundsExceptione){
- checkForComodification();
- thrownewNoSuchElementException();
- }
- }
- publicvoidremove(){
- if(lastRet==-1)
- thrownewIllegalStateException();
- checkForComodification();
- try{
- AbstractList.this.remove(lastRet);//执行remove的操作
- if(lastRet<cursor)
- cursor--;
- lastRet=-1;
- expectedModCount=modCount;//保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常
- }catch(IndexOutOfBoundsExceptione){
- thrownewConcurrentModificationException();
- }
- }
- finalvoidcheckForComodification(){
- if(modCount!=expectedModCount)//当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常
- thrownewConcurrentModificationException();
- }
- }
再看一下ArrayList 的 remove方法
Java代码- publicbooleanremove(Objecto){
- if(o==null){
- for(intindex=0;index<size;index++)
- if(elementData[index]==null){
- fastRemove(index);
- returntrue;
- }
- }else{
- for(intindex=0;index<size;index++)
- if(o.equals(elementData[index])){
- fastRemove(index);
- returntrue;
- }
- }
- returnfalse;
- }
- /*
- *Privateremovemethodthatskipsboundscheckinganddoesnot
- *returnthevalueremoved.
- */
- privatevoidfastRemove(intindex){
- modCount++;//只是修改了modCount,因此modCount将与expectedModCount的值不一致
- intnumMoved=size-index-1;
- if(numMoved>0)
- System.arraycopy(elementData,index+1,elementData,index,
- numMoved);
- elementData[--size]=null;//Letgcdoitswork
- }
回过头去看看java.util.AbstractList的next()方法
Java代码- publicEnext(){
- checkForComodification();//检测modCount和expectedModCount的值!!
- try{
- Enext=get(cursor);
- lastRet=cursor++;
- returnnext;
- }catch(IndexOutOfBoundsExceptione){
- checkForComodification();
- thrownewNoSuchElementException();
- }
- }
- finalvoidcheckForComodification(){
- if(modCount!=expectedModCount)//当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常
- thrownewConcurrentModificationException();
- }
- }
现在真相终于大白了,ArrayList的remove方法只是修改了modCount的值,并没有修改expectedModCount,导致modCount和expectedModCount的值的不一致性,当next()时则抛出ConcurrentModificationException异常
因此使用Iterator遍历集合时,不要改动被迭代的对象,可以使用Iterator本身的方法remove()来删除对象,Iterator.remove()方法会在删除当前迭代对象的同时维护modCount和expectedModCount值的一致性。
解决办法如下:
(1)新建一个集合存放要删除的对象,等遍历完后,调用removeAll(Collection<?> c)方法
把上面例子中迭代集合的代码替换成:
Java代码- List<String>save=newArrayList<String>();
- for(Stringstr:list)
- {
- if(del.contains(str))
- {
- save.add(str);
- }
- }
- list.removeAll(save);
(2)使用Iterator替代增强型for循环:
Java代码- Iterator<String>iterator=list.iterator();
- while(iterator.hasNext()){
- Stringstr=iterator.next();
- if(del.contains(str)){
- iterator.remove();
- }
- }
Iterator.remove()方法保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常。
不过对于在多线程环境下对集合类元素进行迭代修改操作,最好把代码放在一个同步代码块内,这样才能保证modCount和expectedModCount的值的一致性,类似如下:
Java代码- Iterator<String>iterator=list.iterator();
- synchronized(synObject){
- while(iterator.hasNext()){
- Stringstr=iterator.next();
- if(del.contains(str)){
- iterator.remove();
- }
- }
- }
因为迭代器实现类如:ListItr的next(),previous(),remove(),set(E e),add(E e)这些方法都会调用checkForComodification(),源码:
Java代码- finalvoidcheckForComodification(){
- if(modCount!=expectedModCount)
- thrownewConcurrentModificationException();
- }
曾经写了下面这段对HashMap进行迭代删除操作的错误的代码:
Java代码- Iterator<Integer>iterator=windows.keySet().iterator();
- while(iterator.hasNext()){
- inttype=iterator.next();
- windows.get(type).closeWindow();
- iterator.remove();
- windows.remove(type);//
- }
上面的代码也会导致ConcurrentModificationException的发生。罪魁祸首是windows.remove(type);这一句。
根据上面的分析我们知道iterator.remove();会维护modCount和expectedModCount的值的一致性,而windows.remove(type);这句是不会的。其实这句是多余的,上面的代码去掉这句就行了。
iterator.remove()的源码如下:HashIterator类的remove()方法
Java代码- publicvoidremove(){
- if(lastEntryReturned==null)
- thrownewIllegalStateException();
- if(modCount!=expectedModCount)
- thrownewConcurrentModificationException();
- HashMap.this.remove(lastEntryReturned.key);
- lastEntryReturned=null;
- expectedModCount=modCount;//保证了这两值的一致性
- }
HashMap.this.remove(lastEntryReturned.key);这句代码说明windows.remove(type);是多余的,因为已经删除了该key对应的value。
windows.remove(type)的源码:
Java代码- publicVremove(Objectkey){
- if(key==null){
- returnremoveNullKey();
- }
- inthash=secondaryHash(key.hashCode());
- HashMapEntry<K,V>[]tab=table;
- intindex=hash&(tab.length-1);
- for(HashMapEntry<K,V>e=tab[index],prev=null;
- e!=null;prev=e,e=e.next){
- if(e.hash==hash&&key.equals(e.key)){
- if(prev==null){
- tab[index]=e.next;
- }else{
- prev.next=e.next;
- }
- modCount++;
- size--;
- postRemove(e);
- returne.value;
- }
- }
- returnnull;
- }
上面的代码中,由于先调用了iterator.remove();所以再调用HashMap的remove方法时,key就已经为null了,所以会执行:removeNullKey();
方法,removeNullKey()源码:
Java代码- privateVremoveNullKey(){
- HashMapEntry<K,V>e=entryForNullKey;
- if(e==null){
- returnnull;
- }
- entryForNullKey=null;
- modCount++;
- size--;
- postRemove(e);
- returne.value;
- }
不过不管是执行removeNullKey()还是key != null,如果直接调用HashMap的remove方法,都会导致ConcurrentModificationException
这个异常的发生,因为它对modCount++;没有改变expectedModCount的值,没有维护维护索引的一致性。
下面引用一段更专业的解释:
Iterator是工作在一个独立的线程中,并且拥有一个mutex锁。Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照fail-fast原则Iterator会马上抛出java.util.ConcurrentModificationException异常。
所以Iterator在工作的时候是不允许被迭代的对象被改变的。但你可以使用Iterator本身的方法remove()来删除对象,Iterator.remove()方法会在删除当前迭代对象的同时维护索引的一致性。
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- 类和 Json对象
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- android异步加载图片
- Android(安卓)View绘制机制
- android MediaPlayer 错误代码(error code)总结
- android开机启动代码
- Vuforia How To Use Android(安卓)Plugins in Unity Apps