`

遍历并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.util.ConcurrentModificationException` 异常,因为在遍历 HashMap 的元素过程中删除了当前所在元素,下一个待访问的元素的指针也由此丢失了。 2. 正确的删除方法 正确的删除方法是使用迭代...

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

    要解决HashMap遍历删除元素的问题,可以使用Iterator来遍历HashMap,并使用Iterator的remove方法来删除元素。这样可以避免ConcurrentModificationException异常。 小结 在遍历和删除HashMap和List的元素时,需要...

    java哈希遍历_哈希遍历_

    - 遍历HashMap时,由于内部实现细节,顺序可能不稳定,因为哈希函数可能会导致元素在数组中的顺序发生变化。如果需要稳定的遍历顺序,可以考虑使用`LinkedHashMap`,它在保持效率的同时,按照插入顺序或访问顺序遍历...

    Java 实例 - HashMap遍历源代码-详细教程.zip

    - 遍历HashMap时修改HashMap(添加、删除元素)可能会导致`ConcurrentModificationException`,因为迭代器无法检测到这种并发修改。 - 使用`keySet()`遍历并删除元素是安全的,但效率较低,因为这涉及到两次查找:...

    hashMap利用iterator迭代器迭代元素方法

    在使用`Iterator`时,要注意不要在遍历过程中修改`HashMap`,因为这可能会抛出`ConcurrentModificationException`。如果必须修改,可以使用`Iterator`的`remove()`方法,或者使用Java 8引入的流(Stream)API,这样...

    深入解读大厂java面试必考点之HashMap全套学习资料

    5. **迭代器遍历**:HashMap的迭代器是fail-fast类型的,即当在遍历过程中修改了HashMap,迭代器会抛出`ConcurrentModificationException`异常。若要在遍历期间修改HashMap,应使用`Iterator`的`remove()`方法。 6....

    hashmap.zip

    4. **扩容机制**:当HashMap需要扩容时,它会创建一个新的两倍大小的数组,并将旧数组中的所有元素重新哈希到新数组中。这个过程可能会导致原本相邻的元素不再相邻,从而降低链表的长度,提高查找效率。 5. **碰撞...

    HashMap资料.zip

    6. **迭代器**:HashMap提供了键值对的迭代器,但迭代过程中若进行添加、删除或修改操作,可能会抛出`ConcurrentModificationException`。推荐使用`Iterator`的`remove()`方法来安全地删除元素。 7. **HashMap与...

    hashmap_use_in_JDK7.zip

    7. **迭代器遍历**:HashMap的迭代器(`Iterator`)是弱一致性的,这意味着在遍历过程中,如果其他线程修改了HashMap(除了通过迭代器自身的`remove()`方法),则可能会看到不一致的结果。但不会抛出`...

    java.util.ConcurrentModificationException 解决方法

    java.util.ConcurrentModificationException 解决方法 在使用iterator.hasNext()操作迭代器的时候,如果此时迭代的对象发生改变,比如插入了新数据,或者有数据被删除。 则使用会报以下异常: Java.util....

    hashmap和hashtable的区别.docx

    - HashMap 的迭代器(Iterator)是 fail-fast 的,这意味着如果在遍历过程中有其他线程修改了 HashMap(除了迭代器自身的 `remove()` 方法),迭代器将抛出 ConcurrentModificationException。 - 而 Hashtable 的 ...

    经典Java集合笔试选择题10道.doc

    在ArrayList中删除特定元素时,需要注意迭代器的使用,以避免ConcurrentModificationException。A选项从后向前删除元素,避免了这个问题,因此A是正确的。B选项从前往后删除元素时,可能会导致索引超出范围。C选项...

    Java集合容器面试题

    使用Iterator的remove()方法可以边遍历边移除Collection中的元素。 Iterator和ListIterator有什么区别 Iterator和ListIterator有什么区别?Iterator是基本的迭代器,ListIterator是List接口的迭代器,提供了更多的...

    各容器与迭代器的用法.docx

    容器是用来存储和管理对象的集合类,而迭代器则是用来遍历和访问容器中元素的接口。本篇将详细介绍几种常见的容器类(如LinkedList、Vector、ArrayList和Hashtable)以及它们的迭代器用法。 1. **LinkedList**: ...

    出现java.util.ConcurrentModificationException 问题及解决办法

    这个异常的产生是由于集合类(如HashMap)的非线程安全特性,当你在一个线程中使用迭代器遍历集合,而另一个线程在同时修改这个集合时,就会触发此异常。下面我们将深入探讨这个问题的原因、示例代码以及解决策略。 ...

    【IT十八掌徐培成】Java基础第10天-04.List-迭代器-对象等价性.zip

    上述代码展示了如何使用迭代器遍历并可能删除List中的元素。 6. **注意事项**:在使用迭代器时,要特别注意不要同时使用迭代器和直接调用List的`add()`或`remove()`方法,因为这可能导致并发修改异常。如果需要在...

    Java程序员集合框架面试题-java集合框架面试题.docx

    `ListIterator`扩展了`Iterator`接口,专门用于遍历`List`类型的集合,支持双向遍历,可以向前和向后移动,并且能够修改集合中的元素,这在处理列表时非常有用。 4. **HashMap与HashTable的区别** - `HashMap`...

    HashMap 和 Hashtable的区别

    - `HashMap` 的迭代器是 fail-fast 的,即在迭代过程中,如果其他线程修改了 `HashMap` 的结构(添加或删除元素),迭代器会抛出 `ConcurrentModificationException`。但迭代器自身的 `remove()` 方法移除元素则...

    java-集合-知识点汇总

    * 删除元素:使用remove()方法将元素从集合中删除。 * 查找元素:使用contains()方法检查元素是否存在于集合中。 * 遍历集合:使用Iterator或foreach语句遍历集合中的元素。 Java集合的实现 Java集合的实现主要有...

Global site tag (gtag.js) - Google Analytics