`
Arron.li
  • 浏览: 134171 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

解决ArrayList的ConcurrentModificationException

    博客分类:
  • Java
阅读更多

1 问题 :在list<String> 中清空所有的记录,只使用使用单循环,不使用removeAll()

 

2 可以写出五种方式,代码如下:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListRemoveTest {
	 public static void main(String[] args) {  
		         ListRemoveTest test = new ListRemoveTest();  
		           
		         System.out.println("-1-使用jdk5.0以后的增强for循环去remove");  
		         List<String> list = test.buildList();  
		         try {  
		             for (String str : list) {  
		                 list.remove(str);  
		             }  
		         } catch (Exception e) {  
		             // java.util.ConcurrentModificationException  
		             e.printStackTrace();   
		         }  
		   
		         System.out.println("-2-使用Iterator的remove");  
		         list = test.buildList();  
		         try {  
		             Iterator<String> iterator = list.iterator();  
		             while (iterator.hasNext()) {
		            	 iterator.next();
		                 iterator.remove();  
		             }  
		         } catch (Exception e) {  
		             // java.lang.IllegalStateException  
		             e.printStackTrace();  
		         }  
		   
		         System.out.println("-3-iterator遍历+list的remove");  
		         try {  
		             list = test.buildList();  
		             for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {  
		                 String str = (String) iterator.next();  
		                 list.remove(str);  
		             }  
		         } catch (Exception e) {  
		             // java.util.ConcurrentModificationException  
		             e.printStackTrace();  
		         }  
		           
		         System.out.println("-4-使用list的remove(int)方法. [由后向前删除]");  
		         list = test.buildList();  
		         for (int i = list.size(); i > 0; i--) {  
		             list.remove(i - 1);  
		         }  
		   
		         System.out.println("-5-使用list的remove(int)方法. [由前向后删除]");  
		         list = test.buildList();  
		         for (int i = 0; i < list.size(); i++) {  
		             list.remove(0);  
		         }  
		     }  
		   
		     private List<String> buildList() {  
		         List<String> list = new ArrayList<String>();  
		         list.add("a");  
		         list.add("b");  
		         list.add("c");  
		         return list;  
		     }  
}

 3运行结果如下:

-1-使用jdk5.0以后的增强for循环去remove
java.util.ConcurrentModificationException
-2-使用Iterator的remove
-3-iterator遍历+list的remove
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:781)
	at java.util.ArrayList$Itr.next(ArrayList.java:753)
	at com.jdk.ListRemoveTest.main(ListRemoveTest.java:14)
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:781)
	at java.util.ArrayList$Itr.next(ArrayList.java:753)
	at com.jdk.ListRemoveTest.main(ListRemoveTest.java:39)
-4-使用list的remove(int)方法. [由后向前删除]
-5-使用list的remove(int)方法. [由前向后删除]

2,4,5运行正常,1,3抛出

java.util.ConcurrentModificationException

 

4问题原因:

1,3都是因为list的长度改变,Iterator执行next()方法时,调用checkForComodification()时出错,1,3是同一个问题,这两个方法remove操作都是执行的是ArrayList中的remove方法,根本原因在于expectedModCount与modCount他们的不相等,由于执行了ArrayList中的remove(),modCount在每一次循环值会发生改变,而expectedModCount并没有发生,在执行checkForComodification()方法就会抛出异常。

2之所以正确运行是因为调用了Iterator的remove方法,4,5不会执行checkForComodification()操作,所以不会出现这种异常。

 

5结论 :在执行remove()不要将ArrayList 与Interator混合使用,单独使用Interator以及ArrayList的删除都是OK的

6参考的源码

Iterator的next()方法:

        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];
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

 Iterator的remove()方法:

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                //当执行remove操作后,将改变的modCount值重新赋给expectedModCount
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

 ArrayList的remove()方法:

 

 

  /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
 public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        //此处modCount发生了改变,但expectedModCount仍然未发生变化,所以再执行下一次循环时执行
        //Interator的next()方法当然会报错
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
    }
2
0
分享到:
评论
3 楼 jptiancai 2016-02-02  
提供下第六种做法,多用些内存,遍历set的时候,用list删除
         System.out.println("-6-使用set"); 
        
         Set<String> s = new HashSet<String>();
         s.addAll(list);
         for (String str : s) {
        list.remove(str);
}
2 楼 Arron.li 2010-04-16  
首先非常感谢一楼的建议,自己确实对这个数据结构的本质理解的不够深刻,我会继续认真分析这个数据结构,希望读者多提您的建议和参考。
1 楼 sdh5724 2010-04-16  
虽然说对了现象, 也规避了现象。问题分析的角度完全不对。 关键还是要理解这个数据结构构建的原理。 ConcurrentModificationException 这错误本身是为“提醒”程序员并发修改数据结构使用的, 因为这个不是写线程安全的数据结构, 不是为了这个循环删除的目的。从本质上去理解, 你会轻松很多。

相关推荐

    Java源码解析ArrayList及ConcurrentModificationException

    今天小编就为大家分享一篇关于Java源码解析ArrayList及ConcurrentModificationException,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

    ArrayList.md

    老猿说说-ArrayList MD文件 1. 所有的操作都是线程安全的,我们在使用时,无需再加锁; 2. 多个线程同时进行put、remove等操作时并不会阻塞,可以同时进行,和HashTable不同,HashTable在操作时,会锁住整个Map; 3. ...

    ArrayList.java

    iterator和listIterator方法是快速失败的 :如果列表在任何时间从结构上修改创建迭代器之后,以任何方式,除了通过迭代器自身的remove或add方法,迭代器都将抛出ConcurrentModificationException 。 因此,在并发的...

    JDKAPI18CN(中文版)

    应用程序可以添加大量使用ensureCapacity操作元件的前增大ArrayList实例的容量。 这可能会减少增量重新分配的数量。 请注意,此实现不同步。 如果多个线程同时访问884457282749实例,并且至少有一个线程在结构上...

    Java集合教程吐血整理干货.md

    ArrayList的特点 Vector的特点 LinkedList的特点 Set ConcurrentModificationException异常 线程安全的集合 线程安全的 List CopyOnWriteArrayList 线程安全的Set 线程安全的Map ConcurrentHashMap ...

    多线程中使用Java集合类

    Java集合类中,某个线程在 Collection 上进行迭代时...  因此,当一个线程试图ArrayList的数据的时候,另一个线程对ArrayList在进行迭代的,会出错,抛出ConcurrentModificationException。  比如下面的代码:  

    java7hashmap源码-Rebuild-Java:再度重修JAVA

    迭代时被修改抛出ConcurrentModificationException异常 迭代时集合被修改不抛出异常 使用原集合遍历集合元素 使用原集合的副本遍历集合元素 迭代器不要求额外的内存 迭代器需要额外的内存克隆集合对象 示例:...

    Java中CopyOnWriteArrayList的使用

    java中,List在遍历的时候,如果被修改了会抛出java.util.ConcurrentModificationException错误。  看如下代码: import java.util.ArrayList; import java.util.List; public class Resource3 { public ...

    解析Java的迭代器中的fast-fail错误检测机制

    那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。 fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-...

    java 面试题 总结

    多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang....

    JAVA面试题最全集

    一、Java基础知识 1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 2.字符串的操作: 写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入...

    超级有影响力霸气的Java面试题大全文档

    多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 5、String是最基本的数据类型吗?  基本数据类型包括byte、int、char、long、float、double、boolean和short。  java....

Global site tag (gtag.js) - Google Analytics