HashMap不应该是线程安全的,那么为什么如果有人修改了hashMap,迭代器会抛出concurrentmodificationexception
?
而ConcurrentHashMap则不会抛出此异常。
迭代器的实现是否对不同的数据结构不同,还是这些数据结构中有某些方法会抛出ConcurrentModificationException
?
HashMap不应该是线程安全的,那么为什么如果有人修改了hashMap,迭代器会抛出concurrentmodificationexception
?
而ConcurrentHashMap则不会抛出此异常。
迭代器的实现是否对不同的数据结构不同,还是这些数据结构中有某些方法会抛出ConcurrentModificationException
?
当在遍历HashMap
时修改了其结构(例如添加或删除条目),迭代器可能会出现多种故障。
ConcurrentModificationException
异常旨在使任何迭代器由于这些修改而快速失败。
这就是modCount
字段的作用:
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount;
这种行为不仅限于 Map
,在迭代过程中修改元素的 Collection
也会抛出此异常。
不要被误导以为Concurrent
仅与多线程有关。
该异常意味着在使用修改前创建的迭代器时,集合已经进行了结构性修改。这个修改可能是由不同的线程执行的,但也可能是执行迭代的同一个线程。
以下单线程代码会产生CME:
Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
for (String k: map.entrySet()) {
map.clear();
}
Iterator<String> it = map.entrySet().iterator();
while (it.hasNext()) {
String k = it.next();
map.clear();
}
map.clear()
之后调用it.hasNext()
会导致ConcurrentModificationException
异常。迭代器在不同的数据结构中实现是否不同,或者这些数据结构中有一种方法会抛出ConcurrentModificationException异常?
是的,Collection
类中有不同的Iterator
实现。
例如,HashMap
类(内部使用HashIterator
),ConcurrentHashMap
(内部使用KeyIterator
、ValueIterator
等),ArrayList
(使用AbstractList
中的Iterator
)等。
HashMap的迭代器与ConcurrentHashMap的迭代器实现不同。
HashMap的迭代器维护一个版本号(expectedModCount
),并验证checkForComodification()
,如下所示:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
因此,在迭代过程中,如果底层集合的大小被修改(通过添加/删除元素),那么Iterator
会像上面所示一样抛出ConcurrentModificationException
。
而ConcurrentHashMap
的Iterator
实现不执行上述检查,因此它不会抛出ConcurrentModificationException
。您还可以从API ConcurrentHashMap
here找到相同的观点。
它们(ConcurrentHashMaps)不会抛出ConcurrentModificationException。但是,迭代器只设计供一个线程使用。
由于HashMap是一种快速失败的集合(java.util包下的所有集合都是快速失败的),如果对结构或键进行任何修改,迭代器会抛出异常,主要是在HashMap的情况下。
我们在HashMap中有一个变量来计算Map中的修改次数。使用该迭代器可以跟踪对集合所做的修改次数。
transient volatile int modCount;