- 浏览: 439076 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
qja:
Iterator.remove()这个方法也会出错的。
java.util.ConcurrentModificationException 出现的原因和解决办法 -
angeli:
List<String> save = new A ...
java.util.ConcurrentModificationException 出现的原因和解决办法 -
大峰子:
灰常感谢 刚好碰到这个问题, 搜了好多都没找到解决方法
python学习笔记-Python交互模式下方向键出现乱码 -
anypwx:
牛哥,怎么找到的,解决了我的报错问题,谢谢
JSONObject NestableRuntimeException -
tp7300:
确实好很多了,谢谢博主。
Failed to install on device 'emulator-5554': timeout
用iterator遍历集合时碰到java.util.ConcurrentModificationException这个异常,
下面以List为例来解释为什么会报java.util.ConcurrentModificationException这个异常,代码如下:
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");
for(String str : list){
if(del.contains(str)) {
list.remove(str);
}
}
}
运行这段代码会出现如下异常:
Exception in thread "main" java.util.ConcurrentModificationException
for(String str : list) 这句话实际上是用到了集合的iterator() 方法
JDK java.util. AbstractList类中相关源码
public Iterator<E> iterator() { return new Itr(); }
java.util. AbstractList的内部类Itr的源码如下:
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方法
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()方法
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)方法
把上面例子中迭代集合的代码替换成:
List<String> save = new ArrayList<String>(); for(String str : list) { if(del.contains(str)) { save.add(str); } } list.removeAll(save);
(2) 使用Iterator替代增强型for循环:
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的值的一致性,类似如下:
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(),源码:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
曾经写了下面这段对HashMap进行迭代删除操作的错误的代码:
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()方法
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)的源码:
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()源码:
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() 方法会在删除当前迭代对象的同时维护索引的一致性。
评论
这个方法也会出错的。
for(String str : list)
{
if(del.contains(str))
{
save.add(str);
}
}
list.removeAll(save); 这个方法也是会出错的
发表评论
-
solr学习笔记-linux下配置solr
2012-05-26 20:03 7934本文地址: http://zhoujianghai. ... -
关于javac编译时出现“非法字符:\65279”的解决方法
2012-04-12 11:25 1917一些文本编辑软件在保存一个以UTF-8编码的文件时 ... -
UrlConnection连接和Socket连接的区别
2011-10-14 15:22 7152关于UrlConnection连接和Socket连接的区别,只 ... -
搜狗的一道关于加密解密的在线测评题目
2011-10-01 15:04 1612是一个信息编码的程序,阅读其encode部分,并补全其deco ... -
java实现快速排序
2011-09-08 11:00 1327/** * 快速排序 * @author zho ... -
java实现大数相乘
2011-09-08 10:41 3508计算大数:1234567891011121 ... -
Java:ArrayList和LinkedList区别
2011-02-15 16:30 1615一般大家都知道ArrayList ... -
java.lang.LinkageError: loader constraint violation: when resolving interface...
2010-10-06 22:12 2346java.lang.LinkageError: load ... -
JAVA多线程
2010-08-07 14:46 1067多线程 线程:是指进程中的一个执行流程。 线程与进程的区别 ... -
struts2常量详解
2010-06-11 08:55 1652struts2的配置文件之struts ... -
深入Java虚拟机:JVM中的Stack和Heap
2010-05-16 15:02 1102在JVM中,内存分为两个部分,Stack(栈)和Heap(堆) ... -
关于java的作用域protected
2010-05-10 19:54 1920在某个类中定义的protected 方法和属性和默认权限方法和 ... -
关于org.springframework.security.AccessDeniedException: Access is denied
2010-05-06 19:34 7775在做系统权限管理时使用了springsecurity,出现了如 ... -
struts2的struts.xml文件中package里的元素排列顺序
2010-04-22 09:52 1896package里元素必须按照一定的顺序排列,排列顺序如下: ... -
struts2自定义404错误页面
2010-04-21 17:50 5326以前做的一个网站,最近服务器后台出现一些异常,问题是客户访问一 ... -
hibernate的主键生成策略(generator)详解
2010-04-14 09:30 1783assigned” 主键由外 ... -
ssh开发关于struts2,action中方法执行两次的问题
2010-04-10 20:58 2345前段时间发现了一个很奇怪的问题,我的项目中关于action中的 ... -
org.hibernate.hql.ast.QuerySyntaxException: xx is not mapped [sql语句]
2010-04-08 10:06 2221今天一时大意,写了下面这条查询语句 final String ... -
Attempted a bean operation on a null object(tomcat5.0以上版本)
2010-03-24 21:45 1881今天做scwcd模拟题的时候,碰到这样一个题目: A serv ... -
解决struts2.1.6+spring2.0增加webservice错误
2010-03-24 14:16 1857由于要在项目中增加webservice,加入xfire后开始狂 ...
相关推荐
Java.util.ConcurrentModificationException 异常问题详解 ConcurrentModificationException 异常是 Java 中一个常见的异常,它发生在 Iterator 遍历集合时,集合同时被修改引起的异常。在 Java 中,集合类如 ...
java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除。 则使用会报以下异常: Java.util....
在Java编程中,`java.util.ConcurrentModificationException` 是一个常见的运行时异常,通常发生在尝试并发修改集合时。这个异常的产生是由于集合类(如HashMap)的非线程安全特性,当你在一个线程中使用迭代器遍历...
在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. **并发编程最佳实践**:包括避免过度同步,减少锁粒度,正确使用线程池,以及...
`java.util.concurrent.ForkJoinPool`和`java.util.concurrent.RecursiveTask`是其核心类。 7. **非阻塞堆栈跟踪(Non-blocking Stack Traces)** 当线程处于等待状态时,Java 7可以生成不包含阻塞信息的堆栈跟踪...
Java并发工具包(java.util.concurrent)是Java编程中不可或缺的一部分,它为多线程环境提供了高效、安全且易用的工具。这个包包含了各种类和接口,帮助开发者编写高效的并发程序,避免了直接操作线程所带来的复杂性...