background
In a conversation, and friends of friends met such a problem: when traversing map, made a step remove operation, and then ConcurrentModificationException exception occurred. From this I point into the source code to explore some. JDK version: 1.8Copy the code
To explore the
Point into the HashMap source found that before the end of each loop, will check modCount values, if modCount changed, will throw ConcurrentModificationException.Copy the code
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0&& (tab = table) ! =null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for(Node<K,V> e = tab[i]; e ! =null; e = e.next)
action.accept(e.key, e.value);
}
if(modCount ! = mc)throw newConcurrentModificationException(); }}Copy the code
So what about modCount? In combination with the description from the Internet search and the source code comment, this parameter is used to quickly find errors and end iterations on a HashMap.Copy the code
/** * 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;
Copy the code
Once you know what this parameter means, click modCount to see where it changes its value. I find these places where I'm going to change modCount. The last method assigned to 0 (reinitialize) is the argument to reinitialize the HashMap, which can be skipped.Copy the code
putVal
Line 661, which is also the method put() actually calls. As we can see from the source code, when the key is present, the oldValue terminating method is returned directly. That is, the modCount will increase only if you insert a new key.Copy the code
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if(p.hash == hash && ((k = p.key) == key || (key ! =null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k))))
break; p = e; }}if(e ! =null) { // existing mapping for key
V oldValue = e.value;
if(! onlyIfAbsent || oldValue ==null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
Copy the code
removeNode
Line 845, also the method that remove() actually calls. ModCount is changed when data is deleted.Copy the code
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if((tab = table) ! =null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) ! =null) {
Node<K,V> node = null, e; K k; V v;
if(p.hash == hash && ((k = p.key) == key || (key ! =null && key.equals(k))))
node = p;
else if((e = p.next) ! =null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while((e = e.next) ! =null); }}if(node ! =null&& (! matchValue || (v = node.value) == value || (value ! =null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
returnnode; }}return null;
}
Copy the code
clear
Line 860. Reset each node in the map to null. And it changes the modCount value when it's called.Copy the code
public void clear(a) {
Node<K,V>[] tab;
modCount++;
if((tab = table) ! =null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null; }}Copy the code
computeIfAbsent
Line 1142. This method checks if the key exists, and if it does not, adds and modCount++.Copy the code
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
if (mappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) ! =null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while((e = e.next) ! =null);
}
V oldValue;
if(old ! =null&& (oldValue = old.value) ! =null) {
afterNodeAccess(old);
return oldValue;
}
}
V v = mappingFunction.apply(key);
if (v == null) {
return null;
} else if(old ! =null) {
old.value = v;
afterNodeAccess(old);
return v;
}
else if(t ! =null)
t.putTreeVal(this, tab, hash, key, v);
else {
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
return v;
}
Copy the code
compute
Line 1214. Similar to computeIfAbsen(), it is added and modCount++ only if the key does not exist and the value returned by remappingFunction is not empty.Copy the code
@Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) ! =null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while((e = e.next) ! =null);
}
}
V oldValue = (old == null)?null : old.value;
V v = remappingFunction.apply(key, oldValue);
if(old ! =null) {
if(v ! =null) {
old.value = v;
afterNodeAccess(old);
}
else
removeNode(hash, key, null.false.true);
}
else if(v ! =null) {
if(t ! =null)
t.putTreeVal(this, tab, hash, key, v);
else {
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return v;
}
Copy the code
merge
Line 1273. If the key does not exist, key-value is added and modCount++ is added.Copy the code
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException();
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
if ((first = tab[i = (n - 1) & hash]) ! =null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while((e = e.next) ! =null); }}if(old ! =null) {
V v;
if(old.value ! =null)
v = remappingFunction.apply(old.value, value);
else
v = value;
if(v ! =null) {
old.value = v;
afterNodeAccess(old);
}
else
removeNode(hash, key, null.false.true);
return v;
}
if(value ! =null) {
if(t ! =null)
t.putTreeVal(this, tab, hash, key, value);
else {
tab[i] = newNode(hash, key, value, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return value;
}
Copy the code
conclusion
The reinitialize and clear methods reset the entire HashMap, removeNode deletes the data, and everything else adds data. When these operations are performed, the modCount changes. In traverse HashMap, if the above operation will lead to abnormal ConcurrentModificationException.Copy the code