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代码
  1. publicstaticvoidmain(String[]args){
  2. List<String>list=newArrayList<String>();
  3. list.add("1");
  4. list.add("2");
  5. list.add("3");
  6. list.add("4");
  7. list.add("5");
  8. list.add("6");
  9. list.add("7");
  10. List<String>del=newArrayList<String>();
  11. del.add("5");
  12. del.add("6");
  13. del.add("7");
  14. <spanstyle="color:#ff0000;">for(Stringstr:list){
  15. if(del.contains(str)){
  16. list.remove(str);
  17. }
  18. }</span>
  19. }

运行这段代码会出现如下异常:

Java代码
  1. Exceptioninthread"main"java.util.ConcurrentModificationException

for(String str : list) 这句话实际上是用到了集合的iterator()方法

JDK java.util.AbstractList类中相关源码

Java代码
  1. publicIterator<E>iterator(){
  2.   returnnewItr();
  3. }

java.util.AbstractList的内部类Itr的源码如下:

Java代码
  1. privateclassItrimplementsIterator<E>{
  2. /**
  3. *Indexofelementtobereturnedbysubsequentcalltonext.
  4. */
  5. intcursor=0;
  6. /**
  7. *Indexofelementreturnedbymostrecentcalltonextor
  8. *previous.Resetto-1ifthiselementisdeletedbyacall
  9. *toremove.
  10. */
  11. intlastRet=-1;
  12. /**
  13. *ThemodCountvaluethattheiteratorbelievesthatthebacking
  14. *Listshouldhave.Ifthisexpectationisviolated,theiterator
  15. *hasdetectedconcurrentmodification.
  16. */
  17. intexpectedModCount=modCount;
  18. publicbooleanhasNext(){
  19. returncursor!=size();
  20. }
  21. publicEnext(){
  22. checkForComodification();//检测modCount和expectedModCount的值!!
  23. try{
  24. Enext=get(cursor);
  25. lastRet=cursor++;
  26. returnnext;
  27. }catch(IndexOutOfBoundsExceptione){
  28. checkForComodification();
  29. thrownewNoSuchElementException();
  30. }
  31. }
  32. publicvoidremove(){
  33. if(lastRet==-1)
  34. thrownewIllegalStateException();
  35. checkForComodification();
  36. try{
  37. AbstractList.this.remove(lastRet);//执行remove的操作
  38. if(lastRet<cursor)
  39. cursor--;
  40. lastRet=-1;
  41. expectedModCount=modCount;//保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常
  42. }catch(IndexOutOfBoundsExceptione){
  43. thrownewConcurrentModificationException();
  44. }
  45. }
  46. finalvoidcheckForComodification(){
  47. if(modCount!=expectedModCount)//当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常
  48. thrownewConcurrentModificationException();
  49. }
  50. }

再看一下ArrayList 的 remove方法

Java代码
  1. publicbooleanremove(Objecto){
  2. if(o==null){
  3. for(intindex=0;index<size;index++)
  4. if(elementData[index]==null){
  5. fastRemove(index);
  6. returntrue;
  7. }
  8. }else{
  9. for(intindex=0;index<size;index++)
  10. if(o.equals(elementData[index])){
  11. fastRemove(index);
  12. returntrue;
  13. }
  14. }
  15. returnfalse;
  16. }
  17. /*
  18. *Privateremovemethodthatskipsboundscheckinganddoesnot
  19. *returnthevalueremoved.
  20. */
  21. privatevoidfastRemove(intindex){
  22. modCount++;//只是修改了modCount,因此modCount将与expectedModCount的值不一致
  23. intnumMoved=size-index-1;
  24. if(numMoved>0)
  25. System.arraycopy(elementData,index+1,elementData,index,
  26. numMoved);
  27. elementData[--size]=null;//Letgcdoitswork
  28. }

回过头去看看java.util.AbstractListnext()方法

Java代码
  1. publicEnext(){
  2. checkForComodification();//检测modCount和expectedModCount的值!!
  3. try{
  4. Enext=get(cursor);
  5. lastRet=cursor++;
  6. returnnext;
  7. }catch(IndexOutOfBoundsExceptione){
  8. checkForComodification();
  9. thrownewNoSuchElementException();
  10. }
  11. }
  12. finalvoidcheckForComodification(){
  13. if(modCount!=expectedModCount)//当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常
  14. thrownewConcurrentModificationException();
  15. }
  16. }

现在真相终于大白了,ArrayListremove方法只是修改了modCount的值,并没有修改expectedModCount,导致modCountexpectedModCount的值的不一致性,当next()时则抛出ConcurrentModificationException异常

因此使用Iterator遍历集合时,不要改动被迭代的对象,可以使用Iterator本身的方法remove()来删除对象,Iterator.remove()方法会在删除当前迭代对象的同时维护modCountexpectedModCount值的一致性。

解决办法如下:

(1)新建一个集合存放要删除的对象,等遍历完后,调用removeAll(Collection<?> c)方法

把上面例子中迭代集合的代码替换成:

Java代码
  1. List<String>save=newArrayList<String>();
  2. for(Stringstr:list)
  3. {
  4. if(del.contains(str))
  5. {
  6. save.add(str);
  7. }
  8. }
  9. list.removeAll(save);

(2)使用Iterator替代增强型for循环:

Java代码
  1. Iterator<String>iterator=list.iterator();
  2. while(iterator.hasNext()){
  3. Stringstr=iterator.next();
  4. if(del.contains(str)){
  5. iterator.remove();
  6. }
  7. }

Iterator.remove()方法保证了modCountexpectedModCount的值的一致性,避免抛出ConcurrentModificationException异常。

不过对于在多线程环境下对集合类元素进行迭代修改操作,最好把代码放在一个同步代码块内,这样才能保证modCountexpectedModCount的值的一致性,类似如下:

Java代码
  1. Iterator<String>iterator=list.iterator();
  2. synchronized(synObject){
  3. while(iterator.hasNext()){
  4. Stringstr=iterator.next();
  5. if(del.contains(str)){
  6. iterator.remove();
  7. }
  8. }
  9. }

因为迭代器实现类如:ListItr的next(),previous(),remove(),set(E e),add(E e)这些方法都会调用checkForComodification(),源码:

Java代码
  1. finalvoidcheckForComodification(){
  2. if(modCount!=expectedModCount)
  3. thrownewConcurrentModificationException();
  4. }

曾经写了下面这段对HashMap进行迭代删除操作的错误的代码:

Java代码
  1. Iterator<Integer>iterator=windows.keySet().iterator();
  2. while(iterator.hasNext()){
  3. inttype=iterator.next();
  4. windows.get(type).closeWindow();
  5. iterator.remove();
  6. windows.remove(type);//
  7. }

上面的代码也会导致ConcurrentModificationException的发生。罪魁祸首是windows.remove(type);这一句。

根据上面的分析我们知道iterator.remove();会维护modCountexpectedModCount的值的一致性,而windows.remove(type);这句是不会的。其实这句是多余的,上面的代码去掉这句就行了。

iterator.remove()的源码如下:HashIterator类的remove()方法

Java代码
  1. publicvoidremove(){
  2. if(lastEntryReturned==null)
  3. thrownewIllegalStateException();
  4. if(modCount!=expectedModCount)
  5. thrownewConcurrentModificationException();
  6. HashMap.this.remove(lastEntryReturned.key);
  7. lastEntryReturned=null;
  8. expectedModCount=modCount;//保证了这两值的一致性
  9. }

HashMap.this.remove(lastEntryReturned.key);这句代码说明windows.remove(type);是多余的,因为已经删除了该key对应的value。

windows.remove(type)的源码:

Java代码
  1. publicVremove(Objectkey){
  2. if(key==null){
  3. returnremoveNullKey();
  4. }
  5. inthash=secondaryHash(key.hashCode());
  6. HashMapEntry<K,V>[]tab=table;
  7. intindex=hash&(tab.length-1);
  8. for(HashMapEntry<K,V>e=tab[index],prev=null;
  9. e!=null;prev=e,e=e.next){
  10. if(e.hash==hash&&key.equals(e.key)){
  11. if(prev==null){
  12. tab[index]=e.next;
  13. }else{
  14. prev.next=e.next;
  15. }
  16. modCount++;
  17. size--;
  18. postRemove(e);
  19. returne.value;
  20. }
  21. }
  22. returnnull;
  23. }

上面的代码中,由于先调用了iterator.remove();所以再调用HashMap的remove方法时,key就已经为null了,所以会执行:removeNullKey();

方法,removeNullKey()源码:

Java代码
  1. privateVremoveNullKey(){
  2. HashMapEntry<K,V>e=entryForNullKey;
  3. if(e==null){
  4. returnnull;
  5. }
  6. entryForNullKey=null;
  7. modCount++;
  8. size--;
  9. postRemove(e);
  10. returne.value;
  11. }

不过不管是执行removeNullKey()还是key != null,如果直接调用HashMap的remove方法,都会导致ConcurrentModificationException

这个异常的发生,因为它对modCount++;没有改变expectedModCount的值,没有维护维护索引的一致性。

下面引用一段更专业的解释:

Iterator是工作在一个独立的线程中,并且拥有一个mutex锁。Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照fail-fast原则Iterator会马上抛出java.util.ConcurrentModificationException异常。
所以Iterator在工作的时候是不允许被迭代的对象被改变的。但你可以使用Iterator本身的方法remove()来删除对象,Iterator.remove()方法会在删除当前迭代对象的同时维护索引的一致性。

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. 类和 Json对象
  3. Python list sort方法的具体使用
  4. python list.sort()根据多个关键字排序的方法实现
  5. android异步加载图片
  6. Android(安卓)View绘制机制
  7. android MediaPlayer 错误代码(error code)总结
  8. android开机启动代码
  9. Vuforia How To Use Android(安卓)Plugins in Unity Apps

随机推荐

  1. android小记之Animation4种动画效果(贴上
  2. Android(安卓)App 实现分享功能及将应用
  3. 使用BroadcasterRecevier拦截系统短信息_
  4. android 玩转ContentProvider之一--实现Con
  5. Android友盟分享的调不起分享面板,友盟分
  6. Android(安卓)精通 Android(安卓)Data Bi
  7. Android(安卓)NDK开发基础
  8. Android(安卓)- Android(安卓)7.0 拍照,相
  9. 微信小程序多列列表 类似Android的GridVi
  10. 谈谈Android的三种网络通信方式