用iterator遍历集合时碰到java.util.ConcurrentModificationException这个异常, 下面以List为例来解释为什么会报java.util.ConcurrentModificationException这个异常,代码如下: Java代码 public static void main(String[] args) { List<String> list = new ArrayList<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 = new ArrayList<String>(); del.add("5"); del.add("6"); del.add("7"); <span style="color: #ff0000;">for(String str : list){ if(del.contains(str)) { list.remove(str); } }</span> } 运行这段代码会出现如下异常: Java代码 Exception in thread "main" java.util.ConcurrentModificationException for(String str : list) 这句话实际上是用到了集合的iterator() 方法 JDK java.util. AbstractList类中相关源码 Java代码 public Iterator<E> iterator() { return new Itr(); } java.util. AbstractList的内部类Itr的源码如下: Java代码 private class Itr implements Iterator<E> { /** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1; /** * The modCount value that the iterator believes that the backing * List should have. If this expectation is violated, the iterator * has detected concurrent modification. */ int expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); //检测modCount和expectedModCount的值!! try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { if (lastRet == -1) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); //执行remove的操作 if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; //保证了modCount和expectedModCount的值的一致性,避免抛出ConcurrentModificationException异常 } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) //当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常 throw new ConcurrentModificationException(); } } 再看一下ArrayList 的 remove方法 Java代码 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++; //只是修改了modCount,因此modCount将与expectedModCount的值不一致 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work } 回过头去看看java.util. AbstractList的next()方法 Java代码 public E next() { checkForComodification(); //检测modCount和expectedModCount的值!! try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } final void checkForComodification() { if (modCount != expectedModCount) //当modCount和expectedModCount值不相等时,则抛出ConcurrentModificationException异常 throw new ConcurrentModificationException(); } } 现在真相终于大白了,ArrayList的remove方法只是修改了modCount的值,并没有修改expectedModCount,导致modCount和expectedModCount的值的不一致性,当next()时则抛出ConcurrentModificationException异常 因此使用Iterator遍历集合时,不要改动被迭代的对象,可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护modCount和expectedModCount值的一致性。 解决办法如下: (1) 新建一个集合存放要删除的对象,等遍历完后,调用removeAll(Collection<?> c)方法 把上面例子中迭代集合的代码替换成: Java代码 List<String> save = new ArrayList<String>(); for(String str : list) { if(del.contains(str)) { save.add(str); } } list.removeAll(save); (2) 使用Iterator替代增强型for循环: Java代码 Iterator<String> iterator = list.iterator(); while(iterator.hasNext()) { String str = 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()) { String str = iterator.next(); if(del.contains(str)) { iterator.remove(); } } } 因为迭代器实现类如:ListItr的next(),previous(),remove(),set(E e),add(E e)这些方法都会调用checkForComodification(),源码: Java代码 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } 曾经写了下面这段对HashMap进行迭代删除操作的错误的代码: Java代码 Iterator<Integer> iterator = windows.keySet().iterator(); while(iterator.hasNext()) { int type = 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代码 public void remove() { if (lastEntryReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); HashMap.this.remove(lastEntryReturned.key); lastEntryReturned = null; expectedModCount = modCount; //保证了这两值的一致性 } HashMap.this.remove(lastEntryReturned.key);这句代码说明windows.remove(type);是多余的,因为已经删除了该key对应的value。 windows.remove(type)的源码: Java代码 public V remove(Object key) { if (key == null) { return removeNullKey(); } int hash = secondaryHash(key.hashCode()); HashMapEntry<K, V>[] tab = table; int index = 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); return e.value; } } return null; } 上面的代码中,由于先调用了iterator.remove();所以再调用HashMap的remove方法时,key就已经为null了,所以会执行:removeNullKey(); 方法,removeNullKey()源码: Java代码 private V removeNullKey() { HashMapEntry<K, V> e = entryForNullKey; if (e == null) { return null; } entryForNullKey = null; modCount++; size--; postRemove(e); return e.value; } 不过不管是执行removeNullKey()还是key != null,如果直接调用HashMap的remove方法,都会导致ConcurrentModificationException 这个异常的发生,因为它对modCount++;没有改变expectedModCount的值,没有维护维护索引的一致性。 下面引用一段更专业的解释: Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。 所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
相关推荐
Java.util.ConcurrentModificationException 异常问题详解 ConcurrentModificationException 异常是 Java 中一个常见的异常,它发生在 Iterator 遍历集合时,集合同时被修改引起的异常。在 Java 中,集合类如 ...
java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除。 则使用会报以下异常: Java.util....
在Java编程中,`java.util.ConcurrentModificationException` 是一个常见的运行时异常,通常发生在尝试并发修改集合时。这个异常的产生是由于集合类(如HashMap)的非线程安全特性,当你在一个线程中使用迭代器遍历...
项目中碰到的,记录一下解决方案
`java.util`包中的`Collection`接口和其子接口(如`List`、`Set`)以及具体的实现类(如`ArrayList`、`LinkedList`等)构成了Java中处理数据集合的核心框架。了解它们的特性和使用方法对于编写高效、可靠的Java程序...
在Java编程中,`ConcurrentModificationException`是一个常见的运行时异常,主要出现在多线程环境下对集合类(如List、Set、Map等)进行并发修改时。然而,这个异常不仅限于多线程环境,即使在单线程中,如果在遍历...
Java语言的Util类详细介绍 Java语言的Util类是Java开发中非常重要的一部分,它提供了一系列的类来实现基本的数据结构,如线性表、链表等。这些类均在java.util包中。 Collection接口是Java中最基本的集合接口,一...
Spring数据mongodb测试 在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException
本篇文章将深入探讨几个常见的JAVA.BUG模式,并提供相应的解决策略和优化技巧。 一、空指针异常(NullPointerException) 这是Java中最常见的错误之一,当尝试访问一个为null的对象的成员时,程序会抛出此异常。...
下面详细解析一下这个问题的原因和解决策略: 1. **原因分析**: - 当你使用`for-each`循环(增强for循环)遍历集合时,实际上底层是通过迭代器实现的。但是,如果你在循环内部直接修改集合(比如删除元素),而...
- `java.util.Iterator`的改进:支持`remove()`操作,避免抛出`ConcurrentModificationException`。 ### 4. 性能优化 JDK 1.6对编译器和垃圾收集器进行了优化,提高了运行效率,例如: - **Server VM的改进**: ...
例如`Collections.unmodifiable*`方法创建的集合和`java.util.Collections`类中的`emptyList()`、`emptySet()`等。这些集合一旦创建就不能修改,因此天然线程安全。 示例代码: ```java import java.util.*; ...
java.util.ConcurrentModificationException: mutation occurred during iteration [error] scala.collection.mutable.MutationTracker$.checkMutations(MutationTracker.scala:43) [error] scala.collection....
3. 使用`java.util.concurrent.ConcurrentHashMap`代替HashMap,它在并发环境下提供高效且线程安全的访问和修改。 4. 如果需要更复杂的并发控制,可以使用`java.util.concurrent.locks`包下的Lock接口及其实现,如...
### 迭代大师的修炼之道:Java中Iterator与Enhanced for loop的深度解析 #### 一、引言 在Java编程的世界里,迭代是处理集合数据的重要手段之一。本文将重点探讨两种常用的迭代方式——`Iterator`接口和`Enhanced ...
在Java编程语言中,集合框架(`java.util`包)提供了多种容器类来存储对象,如`List`、`Set`和`Map`等。为了遍历这些容器中的元素,Java引入了迭代器模式(Iterator Pattern),这是一种常用的设计模式,它提供了一...
9. **并发异常处理**:在并发编程中,死锁、活锁、饥饿等是常见的问题,理解这些异常并学会避免和解决是提升并发编程能力的关键。 10. **并发编程最佳实践**:包括避免过度同步,减少锁粒度,正确使用线程池,以及...