HashMap中modCount屬性用實(shí)現(xiàn)fail-fast機(jī)制,即遍歷過(guò)程如果有增刪改,則馬上拋出異常。每次增刪改modCount加一。
KeySet和Values中都有forEach方法。
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (Node<K,V> e : tab) {
for (; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
代碼可知forEach是全部遍歷完才比較,所以這個(gè)不算是fail-fast。
KeySet和Values中Iterator<K> iterator() 方法實(shí)現(xiàn)Iterator接口。
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
removeNode(p.hash, p.key, null, false, false);
expectedModCount = modCount;
}
expectedModCount初始化為modCount
nextNode()中會(huì)先判斷是否變化,變化則直接fail-fast
remove()比較特殊,先判斷是否變化,變化則直接fail-fast。然后刪除,這時(shí)modCount會(huì)變化。最后再expectedModCount = modCount;這樣避免了fail-fast。
所以如果遍歷同時(shí)修改則必須調(diào)用iterator的remove方法才可以。
如果遍歷時(shí)直接調(diào)用HashMap的remove方法會(huì)拋出異常。
forEach雖然也會(huì)拋出異常,但是并不是fail-fast。