Java HashMap 如何正确遍历并删除元素

(一)HashMap的遍历

  HashMap的遍历主要有两种方式:

  第一种采用的是foreach模式,适用于不需要修改HashMap内元素的遍历,只需要获取元素的键/值的情况。

HashMap<K, V> myHashMap;
for (Map.entry<K, V> item : myHashMap.entrySet()){
    K key = item.getKey();
    V val = item.getValue();
    //todo with key and val
    //WARNING: DO NOT CHANGE key AND val IF YOU WANT TO REMOVE ITEMS LATER
}


  第二种采用迭代器遍历,不仅适用于HashMap,对其它类型的容器同样适用。

  采用这种方法的遍历,可以用下文提及的方式安全地对HashMap内的元素进行修改,并不会对后续的删除操作造成影响。

for (Iterator<Map.entry<K, V>> it = myHashMap.entrySet().iterator; it.hasNext();){
    Map.Entry<K, V> item = it.next();
    K key = item.getKey();
    V val = item.getValue();
    //todo with key and val
    //you may remove this item using  "it.remove();"
}


(二)HashMap之删除元素

  如果采用第一种的遍历方法删除HashMap中的元素,Java很有可能会在运行时抛出异常。

HashMap<String, Integer> myHashMap = new HashMap<>();
myHashMap.put("1", 1);
myHashMap.put("2", 2);
for (Map.Entry<String, Integer> item : myHashMap.entrySet()){
    myHashMap.remove(item.getKey());
}
for (Map.Entry<String, Integer> item : myHashMap.entrySet()){
    System.out.println(item.getKey());
}

  运行上面的代码,Java抛出了 java.util.ConcurrentModificationException 的异常。并附有如下信息。

at java.util.HashMap$HashIterator.nextNode(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)

  可以推测,由于我们在遍历HashMap的元素过程中删除了当前所在元素,下一个待访问的元素的指针也由此丢失了。



  所以,我们改用第二种遍历方式。

  代码如下:

for (Iterator<Map.Entry<String, Integer>> it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<String, Integer> item = it.next();
    //... todo with item
    it.remove();
}
for (Map.Entry<String, Integer> item : myHashMap.entrySet()){
    System.out.println(item.getKey());
}

  运行结果没有显示,表明HashMap中的元素被正确删除了。



(三)在HashMap的遍历中删除元素的特殊情况

  上述方法可能足以应付多数的情况,但是如果你的HashMap中的键值同样是一个HashMap,假设你需要处理的是 HashMap<HashMap<String, Integer>, Double> myHashMap 时,很不碰巧,你可能需要修改myHashMap中的一个项的键值HashMap中的某些元素,之后再将其删除。

  这时,单单依靠迭代器的 remove() 方法是不足以将该元素删除的。

  例子如下:

HashMap<HashMap<String, Integer>, Integer> myHashMap = new HashMap<>();
HashMap<String, Integer> temp = new HashMap<>();
temp.put("1", 1);
temp.put("2", 2);
myHashMap.put(temp, 3);
for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>>
    it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<HashMap<String, Integer>, Integer> item = it.next();
    item.getKey().remove("1");
    System.out.println(myHashMap.size());
    it.remove();
    System.out.println(myHashMap.size());
}

  结果如下:

1
1

  虽然 it.remove(); 被执行,但是并没有真正删除元素。

  原因在于期望删除的元素的键值(即 HashMap<String, Integer> temp )被修改过了。



  解决方案:

  既然在这种情况下,HashMap中被修改过的元素不能被删除,那么不妨直接把待修改的元素直接删除,再将原本所需要的“修改过”的元素加入HashMap。

  想法很好,代码如下:

for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>>
    it = myHashMap.entrySet().iterator(); it.hasNext();){
    Map.Entry<HashMap<String, Integer>, Integer> item = it.next();
    //item.getKey().remove("1");
    HashMap<String, Integer> to_put = new HashMap<>(item.getKey());
    to_put.remove("1");
    myHashMap.put(to_put, item.getValue());
    System.out.println(myHashMap.size());
    it.remove();
    System.out.println(myHashMap.size());
}

  但是依然是RE:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.remove(Unknown Source)

  原因在于,迭代器遍历时,每一次调用 next() 函数,至多只能对容器修改一次。上面的代码则进行了两次修改:一次添加,一次删除。



  既然 java.util.ConcurrentModificationException 异常被抛出了,那么去想办法拿掉这个异常即可。

  最后的最后,我决定弃HashMap转投ConcurrentHashMap。将myHashMap定义为ConcurrentHashMap之后,其它代码不动。

  运行结果如下:

2
1

  最终,通过ConcurrentHashMap和一些小技巧的使用,变形实现了对被修改过键值的元素的删除。

时间: 2024-08-05 07:06:57

Java HashMap 如何正确遍历并删除元素的相关文章

java 集合遍历时删除元素

本文探讨集合在遍历时删除其中元素的一些注意事项,代码如下 ? 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import java.util.ArrayList; import java.util.Iterator; import java

java实现二叉树及遍历、删除

java实现二叉树及遍历.删除 个人网站:多猫影视[可以看各大网站的VIP视频]www.duomao.xyz 二叉树是递归定义的,其结点有左右子树之分,逻辑上二叉树有五种基本形态: (1)空二叉树——如图(a): (2)只有一个根结点的二叉树——如图(b): (3)只有左子树——如图(c): (4)只有右子树——如图(d): (5)完全二叉树——如图(e). 注意:尽管二叉树与树有许多相似之处,但二叉树不是树的特殊情形.[1] 类型 (1)完全二叉树——若设二叉树的高度为h,除第 h 层外,其它

List的遍历和删除元素

/** * 遍历list的方法 * @param args */ public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("111"); list.add("222"); list.add("333"); list.add("222"); list.add("4

STL容器遍历时删除元素

STL容器遍历时在循环体内删除元素最容易出错了,根本原因都是因为迭代器有效性问题,在此记下通用删除方法,该方法适用于所有容器: 1 std::vector<int> myvec; 2 3 std::vector<int>::iterator it = myvec.begin(); 4 while( it != myvec.end()) 5 { 6 it = myvec.erase(it); 7 } 容器list有个比较另类的删除方法,如下代码所示: std::list<int

python list遍历时删除元素

python list遍历时候删除还真需要注意下,今天帮同学处理数据,竟然傻逼了. 需求: 除了第一列,给每列加一个序号如:"1:0","2:0","3:20100307",然后删除冒号后为0的数据. 推荐做法: arrays = [ ['5001', '0', '0', '20100307', '20150109', '2', '3', '75', '0', '0', '114', '13', '2', '0', '0'], ['10001',

list遍历时删除元素

正如我们前面所知道. 在我们依靠遍历删除ArrayList时总是出现一些神奇的现象.比如 ArrayList<integer> arraylist = new ArrayList<integer>(); arraylist.add(1); arraylist.add(1); arraylist.add(2); arraylist.add(3); arraylist.add(1); 我们遍历并删除其中的1 for(int i = 0:i < arraylist.size();i

python中遍历list删除元素引发的问题与解决办法

引发问题的场景 今天在写一个小游戏的demo时,进行游戏元素操作时,遇到了一个问题.类似下面代码: list = ['a','b','c','d'] # element_type == list for i in list: print('元素的下标为{},元素的值{}'.format(list.index(i),list)) # 打出内容.方便查看 list.remove(i) print(list) 本意是遍历删除list中的所有元素.最后list应该为一个空数组. 但是代码实际运行结果是

java数组-----拷贝、掺入、删除元素

package cn.zxg.arrays2; /** * 测试数组的拷贝 */public class TestArrayCopy { public static void main(String[] args) { TestBaseCopy(); TestBaseCopy2(); String [] str={"阿里","京东","百度","亚马逊"}; removeElment(str,1); } //数组的拷贝 pub

Python遍历字典删除元素

这种方式是一定有问题的: d = {'a':1, 'b':2, 'c':3} for key in d: d.pop(key) 会报这个错误:RuntimeError: dictionary changed size during iteration 这种方式Python2可行,Python3还是报上面这个错误. d = {'a':1, 'b':2, 'c':3} for key in d.keys(): d.pop(key) Python3报错的原因是keys()函数返回的是dict_keys