概述
- package net.nie.test;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- public class HashMapTest {
- private static Map<Integer, String> map=new HashMap<Integer,String>();
- /** 1.HashMap 类映射不保证顺序;某些映射可明确保证其顺序: TreeMap 类
- * 2.在遍历Map过程中,不能用map.put(key,newVal),map.remove(key)来修改和删除元素,
- * 会引发 并发修改异常,可以通过迭代器的remove():
- * 从迭代器指向的 collection 中移除当前迭代元素
- * 来达到删除访问中的元素的目的。
- * */
- public static void main(String[] args) {
- map.put(1,"one");
- map.put(2,"two");
- map.put(3,"three");
- map.put(4,"four");
- map.put(5,"five");
- map.put(6,"six");
- map.put(7,"seven");
- map.put(8,"eight");
- map.put(5,"five");
- map.put(9,"nine");
- map.put(10,"ten");
- Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
- while(it.hasNext()){
- Map.Entry<Integer, String> entry=it.next();
- int key=entry.getKey();
- if(key%2==1){
- System.out.println("delete this: "+key+" = "+key);
- //map.put(key, "奇数"); //ConcurrentModificationException
- //map.remove(key); //ConcurrentModificationException
- it.remove(); //OK
- }
- }
- //遍历当前的map;这种新的for循环无法修改map内容,因为不通过迭代器。
- System.out.println("-------nt最终的map的元素遍历:");
- for(Map.Entry<Integer, String> entry:map.entrySet()){
- int k=entry.getKey();
- String v=entry.getValue();
- System.out.println(k+" = "+v);
- }
- }
- }
只能通过iterator的remove实现删除和增加。
分析原因
其实上面的三种遍历方式从根本上讲都是使用的迭代器,之所以出现不同的结果是由于remove操作的实现不同决定的。
首先前两种方法都在调用nextEntry方法的同一个地方抛出了异常
1 2 3 4 5 6 7 | final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; ... ... } |
这里modCount是表示map中的元素被修改了几次(在移除,新加元素时此值都会自增),而expectedModCount是表示期望的修改次数,在迭代器构造的时候这两个值是相等,如果在遍历过程中这两个值出现了不同步就会抛出ConcurrentModificationException异常。
1、HashMap的remove方法实现
1 2 3 4 | public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } |
2、HashMap.KeySet的remove方法实现
-
public boolean remove(Object o) {
-
return HashMap.this.removeEntryForKey(o) != null;
-
}
3、HashMap.HashIterator的remove方法实现
1 2 3 4 5 6 7 8 9 10 | 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; } |
以上三种实现方式都通过调用HashMap.removeEntryForKey方法来实现删除key的操作。在removeEntryForKey方法内只要移除了key modCount就会执行一次自增操作,此时modCount就与expectedModCount不一致了,上面三种remove实现中,只有第三种iterator的remove方法在调用完removeEntryForKey方法后同步了expectedModCount值与modCount相同,所以在遍历下个元素调用nextEntry方法时,iterator方式不会抛异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | final Entry<K,V> removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key.hashCode()); 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++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; }
return e; } |
发散
1、如果是遍历过程中增加或修改数据呢?
增加或修改数据只能通过Map的put方法实现,在遍历过程中修改数据可以,但如果增加新key就会在下次循环时抛异常,因为在添加新key时modCount也会自增。
2、有些集合类也有同样的遍历问题,如ArrayList,通过Iterator方式可正确遍历完成remove操作,直接调用list的remove方法就会抛异常。
-
//会抛ConcurrentModificationException异常
-
for(String str : list){
-
list.remove(str);
-
}
-
//正确遍历移除方式
-
Iterator<String> it = list.iterator();
-
while(it.hasNext()){
-
it.next();
-
it.remove();
-
}
3、jdk为什么这样设计,只允许通过iterator进行remove操作?
HashMap和keySet的remove方法都可以通过传递key参数删除任意的元素,而iterator只能删除当前元素(current),一旦删除的元素是iterator对象中next所正在引用的,如果没有通过modCount、 expectedModCount的比较实现快速失败抛出异常,下次循环该元素将成为current指向,此时iterator就遍历了一个已移除的过期数据。
转载自https://blog.csdn.net/e01014165/article/details/52174520
https://blog.csdn.net/weikzhao0521/article/details/53022803?locationNum=10&fps=1
最后
以上就是美满绿草为你收集整理的Java 遍历HashMap并remove、put的全部内容,希望文章能够帮你解决Java 遍历HashMap并remove、put所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复