我们要写个遍历Map集合,删除指定key值的方法,我们估计会这样写。 刚开始我习惯上会写上map.remove(entry.getKey()),remove集合的一个值。但是写了之后,会发现java.util.ConcurrentModificationException异常。
代码语言:javascript复制private static void deleteKeyOfMap(String mkey,Map<String,Object> paramsMap){
Iterator<Map.Entry<String,Object>> iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry<String, Object> entry = iter.next();
if(entry.getKey().equals(k)){
map.remove(entry.getKey());
//iter.remove();
}
}
}
这是什么异常呢? 总结起来其实是集合的安全性决定的,可以看看HashMap的源码
代码语言:javascript复制private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index ]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index ]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
找到源码关键的一句expectedModCount = modCount;
初始化时,modCount赋值给expectedModCount。
跟一下代码:
代码语言:javascript复制 public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);//在跟一下removeEntryForKey方法
expectedModCount = modCount;
}
代码语言:javascript复制/**
* Removes and returns the entry associated with the specified key
* in the HashMap. Returns null if the HashMap contains no mapping
* for this key.
*/
final Entry<K,V> removeEntryForKey(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount ;//modCount 1
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
从源码可以看出,调用HashMap的remove方法modCount加1了,所以会导致 expectedModCount != modCount;
这就是java.util.ConcurrentModificationException出现的原因
集合本身这样设计是为了安全性考虑,在Iterator遍历时,不允许被调用remove等等方法
所以正确的代码应该这样改
代码语言:javascript复制private static void deleteKeyOfMap(String mkey,Map<String,Object> paramsMap){
Iterator<Map.Entry<String,Object>> iter = map.entrySet().iterator();
while(iter.hasNext()){
Map.Entry<String, Object> entry = iter.next();
if(entry.getKey().equals(k)){
//map.remove(entry.getKey());
iter.remove();
}
}
}
附录参考资料:
Java迭代foreach原理解析