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和一些小技巧的使用,变形实现了对被修改过键值的元素的删除。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

时间: 2019-05-29

java 中HashMap、HashSet、TreeMap、TreeSet判断元素相同的几种方法比较

java 中HashMap.HashSet.TreeMap.TreeSet判断元素相同的几种方法比较 1.1     HashMap 先来看一下HashMap里面是怎么存放元素的.Map里面存放的每一个元素都是key-value这样的键值对,而且都是通过put方法进行添加的,而且相同的key在Map中只会有一个与之关联的value存在.put方法在Map中的定义如下. V put(K key, V value); 它用来存放key-value这样的一个键值对,返回值是key在Map中存放的旧va

Java中HashMap和TreeMap的区别深入理解

首先介绍一下什么是Map.在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value.这就是我们平时说的键值对. HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的). HashMap 非线程安全 TreeMap 非线程安全 线程安全 在Java里,线程安全一般体

Java中的HashSet详解和使用示例_动力节点Java学院整理

第1部分 HashSet介绍 HashSet 简介 HashSet 是一个没有重复元素的集合. 它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素. HashSet是非同步的.如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步.这通常是通过对自然封装该 set 的对象执行同步操作来完成的.如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来"包装" set.

Java中HashMap里面key为null存放到哪

我们知道HashMap集合是允许存放null值的 hashMap是根据key的hashCode来寻找存放位置的,那当key为null时, 怎么存储呢? 在put方法里头,其实第一行就处理了key=null的情况. // HashMap的put方法 public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) // key为null调用putForNull

java中Hashmap的get方法使用

目录 java中Hashmap的get方法 举例 HashMap中get方法的原理 1.首先向get()方法中传递一个key 2.在get()方法中调用hash(key) 3.在get()方法中调用getNode(hash,key)方法 4.getNode()方法中 java中Hashmap的get方法 map中存储的是键值对,也就是说通过set方法进行参数和值的存储,之后通过get"键"的形式进行值的读取. 举例 Map map = new Hashmap();//创建一个map m

Java中Stream流去除List重复元素的方法

本文实例为大家分享了Java中Stream流去除List重复元素的具体代码,供大家参考,具体内容如下 业务场景 在开发中我们常常需要过滤List中的重复对象,而重复的定义往往是根据单个条件或者多个条件,如果是单个条件的话还是比较好处理的,即使不使用工具,代码也可以很容易实现,但如果判断依据不是单个条件,而是多个条件的话,代码实现起来就会比较复杂,此时我们一般就会使用工具来简化开发 单条件去重代码 ArrayList<listData> collect = list.stream().colle

java 中HashMap实现原理深入理解

1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端.       数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难: 链表 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N).链表的特点是:寻址困难,插入和删除容易. 哈希表 那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提

浅谈java中HashMap键的比较方式

先看一个例子 Integer integer=12344; Integer integer1=12344; 在Java中Integer 和Integer1是不相等的,但是如果再执行如下语句 map.put(integer, 1); map.put(integer1, 2); 会发现2会把1覆盖,问题来了,明明是两个不同的对象,为什么,2会把1覆盖呢? 我们看HashMap中添加键的源代码,如下 可以发现我们传进来的键交给了一个hash的成员方法区处理,这里我们看看hash方法的源码 哦,看到这里

Java中HashMap的初始容量设置方式

Java中HashMap的初始容量设置 根据阿里巴巴Java开发手册上建议HashMap初始化时设置已知的大小,如果不超过16个,那么设置成默认大小16: 集合初始化时, 指定集合初始值大小. 说明: HashMap使用HashMap(int initialCapacity)初始化 正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1.注意负载因子(即loader factor)默认为0.75, 如果暂时无法确定初始值大小,请设置为16(即默认值). 反例:

Java中HashMap&nbsp;中的一个坑

目录 前言 问题展示 原因分析 解决方案 LinkedHashMap 的魔力 总结 前言 最近公司的系统要增加一个新的列表展示功能,功能本身难度并不大,但遇到了一个很“奇怪”的问题.小伙伴在执行查询列表时,明明已经使用了 order by 进行排序了,但最终查询出来的数据却还是乱的. 预期中的(正确)结果:   现实中的(非预期)结果:  那到底是哪里出现了问题呢? 问题展示 为了方便展示,我把复杂的业务程序简化成了以下代码: import java.util.HashMap; public c