`

遍历并remove HashMap中的元素时,遇到ConcurrentModificationException

阅读更多
遇到异常的代码段
for (Map.Entry<ImageView, UserConcise> peer : mPortraitViewPeers.entrySet()) {
	ImageView key = peer.getKey();
	key.setImageBitmap(peer.getValue().getProfile_image());
	mPortraitViewPeers.remove(key);
}


在查看一些资料后,修改的代码
Iterator<Map.Entry<ImageView, UserConcise>> iterator = mPortraitViewPeers.entrySet().iterator();

while (iterator.hasNext()) {
	Map.Entry<ImageView, UserConcise> entry = iterator.next();
	ImageView key = entry.getKey();
	key.setImageBitmap(entry.getValue().getProfile_image());
	iterator.remove();	
}


以下是网上的解释
java.util.ConcurrentModificationException
工作中碰到个ConcurrentModificationException。代码如下:
List list = ...;
for(Iterator iter = list.iterator(); iter.hasNext();) {
	Object obj = iter.next();
	...
	if(***) {
		list.remove(obj);
	}
}
在执行了remove方法之后,再去执行循环,iter.next()的时候,报java.util.ConcurrentModificationException(当然,如果remove的是最后一条,就不会再去执行next()操作了)

下面来看一下源码
public interface Iterator<E> {
	boolean hasNext();
	E next();
	void remove();
}

public interface Collection<E> extends Iterable<E> {
	...
	Iterator<E> iterator();
	boolean add(E o);
	boolean remove(Object o);
	...
}
这里有两个remove方法

接下来来看看AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {  
//AbstractCollection和List都继承了Collection
	protected transient int modCount = 0;
	private class Itr implements Iterator<E> {  //内部类Itr
		int cursor = 0;
		int lastRet = -1;
		int expectedModCount = modCount;

		public boolean hasNext() {
			return cursor != size();
		}

		public E next() {
			checkForComodification();  //特别注意这个方法
			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;  //重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生
			} catch(IndexOutOfBoundsException e) {
					throw new ConcurrentModificationException();
			}
		}

		final void checkForComodification() {
			if (modCount != expectedModCount)  //当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException
					throw new ConcurrentModificationException();
		}
	}    
}

remove(Object o)在ArrayList中实现如下:
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 void fastRemove(int index) {
	modCount++;  //只增加了modCount
	....
}

所以,产生ConcurrentModificationException的原因就是:
执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。
要避免这个Exception,就应该使用remove()方法。
这里我们就不看add(Object o)方法了,也是同样的原因,但没有对应的add()方法。一般嘛,就另建一个List了


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

相关推荐

    Java HashMap 如何正确遍历并删除元素的方法小结

    主要介绍了Java HashMap 如何正确遍历并删除元素的方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    怎样遍历一个HashMap?

    可以通过2种方法遍历HashMap &lt;br&gt;Map map = new HashMap(); &lt;br&gt;for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { &lt;br&gt; Map.Entry entry = (Map.Entry) iter.next(); &lt;br&gt; Object ...

    java中Map集合的常用遍历方法及HashMap的应用实例

    1、遍历Map.entrySet():它的每一个元素都是Map.Entry对象,这个对象中, 放着的就是Map中的某一对key-value; 2、遍历Map.keySet():它是Map中key值的集合,我们可以通过遍历这个集合来 读取Map中的元素; 3、...

    HashMap和List遍历方法及如何遍历删除元素总结

    在本篇文章中小编给大家分享了关于HashMap和List遍历方法及如何遍历删除元素知识点总结,需要的朋友们参考下。

    java哈希遍历_哈希遍历_

    哈希树遍历 HashMap遍历和使用 HashMap遍历和使用

    HashMap源码实现红黑树添加元素和删除元素

    HashMap 中元素的存储规则是按照 key 的 hash 值找到下标,然后判断该位置是否有元素,如果有就将尾节点的 Node 的 next 指向新的元素;如果没有就直接插入。 红黑树的插入操作: 在红黑树中,插入操作是通过 ...

    Java5种遍历HashMap数据的写法

    主要介绍了Java5种遍历HashMap数据的写法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    HashMap遍历

    讲述了hashmap的几种遍历方法,相当经典

    HashMap 概述 精讲 .md

    看完这篇 HashMap,和面试官扯皮就没问题了 - HashMap 概述 - HashMap 和 HashTable 的区别 - 相同点 - 不同点 - HashMap 和 HashSet 的区别 ... - HashMap 中的移除方法 - 关于 HashMap 的面

    java中HashMap详解

    动态扩容:当HashMap中的元素数量超过了容量(默认为16)与负载因子(默认为0.75)的乘积时,HashMap会自动扩容,即创建一个新的数组,并将原来的元素重新映射到新的数组中。 高性能:由于使用了哈希表,HashMap在查找...

    基于HashMap遍历和使用方法(详解)

    下面小编就为大家带来一篇基于HashMap遍历和使用方法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    java遍历HashMap简单的方法

    主要介绍了java遍历HashMap简单的方法,以实例形式简单分析了采用java遍历HashMap的技巧,具有一定参考借鉴价值,需要的朋友可以参考下

    1.HashSet和HashMap遍历.md

    自己写的例子,关于HashSet遍历和HashMap遍历的. 感谢大家参考

    ArrayList->Map

    遍历ArrayList存入HashMap中

    Hashmap详解

    3. 如果数组中的该索引处已经存在键值对,则循环遍历链表,直到找到相同的键值对。 4. 如果找到了相同的键值对,则将新值覆盖旧值,并返回旧值。 5. 如果没有找到相同的键值对,则将键值对添加到链表中。 Hash 码的...

    Map的四种遍历方法

    该资源摘自: http://www.cnblogs.com/kristain/articles/2033566.html

    Java HashMap的三种遍历方法及优缺点含示例

    当两个或更多的键的哈希值相同时,就会发生哈希冲突,此时,这些键值对就会存储在链表中。 在JDK1.8之前,当链表长度大于阈值(默认为8)时,链表会被转化为红黑树,以减少搜索时间。而在JDK1.8之后,这个阈值被改...

    (002)HashMap$TreeNode之往红黑树添加元素-putTreeVal方法.docx

    HashMap之往红黑树添加元素-putTreeVal方法源码解读:当要put的元素所在数组索引位置已存在元素,且是红黑树类型时,就会调用putTreeVal方法添加元素到红黑树上,具体操作步骤如下: 1. 从根节点开始,到左右子树,...

    HashMap如何添加元素详解

    map接口是一个双边队列,拥有key,value两个属性,其中key在存储的集合中不允许重复,value可以重复。 HashMap特点 存储结构在jdk1.7当中是数组加链表的结构,在jdk1.8当中改为了数组加链表加红黑树的结构。 HashMap...

    使用多种方式实现遍历HashMap的方法

    下面小编就为大家带来一篇使用多种方式实现遍历HashMap的方法。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Global site tag (gtag.js) - Google Analytics