博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于HashMap的遍历输出,以及以遍历形式删除键值对所出现的问题。
阅读量:7271 次
发布时间:2019-06-29

本文共 10333 字,大约阅读时间需要 34 分钟。

  • 一 HashMap遍历输出的几种方式

  1.  foreach 取出map.entrySet()并获取key和value
    1  Map
    map = new HashMap
    ();2 for (Entry
    entry : map.entrySet()) {3 entry.getKey();4 entry.getValue();5 }  

     

  2. 调用map.entrySet()的集合迭代器,通过hasNext()方法判断是否有元素可迭代
    1 Map
    map = new HashMap
    ();2 Iterator
    > iterator = map.entrySet().iterator();3 while (iterator.hasNext()) {4 Map.Entry
    entry = iterator.next();5 entry.getKey();6 entry.getValue();7 }

     

  3. 通过HashMap中的keySet()方法获取key集合,通过循环获取value
    1 Map
    map = new HashMap
    ();2 for (String key : map.keySet()) {3 map.get(key);4 }

     

  4. 通过临时变量保存map.entrySet(),遍历输出
    1 Map
    map = new HashMap
    ();2 Set
    > entrySet = map.entrySet();3 for (Entry
    entry : entrySet) {4 entry.getKey();5 entry.getValue();6 }

     

  以上就是常用的四种遍历输出hashMap集合的方法,接下来分析一下四种方法的适用性和效率。

  •  二 HashMap常用的四种遍历方法的分析对比及实用性

     先贴上代码(代码参考)

1 package com.lwu.java.test;  2    3 import java.text.DecimalFormat;  4 import java.util.Calendar;  5 import java.util.HashMap;  6 import java.util.Iterator;  7 import java.util.Map;  8 import java.util.Map.Entry;  9 import java.util.Set; 10   11 /** 12  * JavaLoopTest 13  *  14  * @author www.trinea.cn 2013-10-28 15      16  */ 17 public class JavaLoopTest { 18   19     public static void main(String[] args) { 20         System.out.print("compare loop performance of HashMap"); 21         loopMapCompare(getHashMaps(10000, 100000, 1000000, 2000000)); 22     } 23   24     public static Map
[] getHashMaps(int... sizeArray) { 25 Map
[] mapArray = new HashMap[sizeArray.length]; 26 for (int i = 0; i < sizeArray.length; i++) { 27 int size = sizeArray[i]; 28 Map
map = new HashMap
(); 29 for (int j = 0; j < size; j++) { 30 String s = Integer.toString(j); 31 map.put(s, s); 32 } 33 mapArray[i] = map; 34 } 35 return mapArray; 36 } 37 38 public static void loopMapCompare(Map
[] mapArray) { 39 printHeader(mapArray); 40 long startTime, endTime; 41 42 // Type 1 43 for (int i = 0; i < mapArray.length; i++) { 44 Map
map = mapArray[i]; 45 startTime = Calendar.getInstance().getTimeInMillis(); 46 for (Entry
entry : map.entrySet()) { 47 entry.getKey(); 48 entry.getValue(); 49 } 50 endTime = Calendar.getInstance().getTimeInMillis(); 51 printCostTime(i, mapArray.length, "for each entrySet", endTime - startTime); 52 } 53 54 // Type 2 55 for (int i = 0; i < mapArray.length; i++) { 56 Map
map = mapArray[i]; 57 startTime = Calendar.getInstance().getTimeInMillis(); 58 Iterator
> iterator = map.entrySet().iterator(); 59 while (iterator.hasNext()) { 60 Map.Entry
entry = iterator.next(); 61 entry.getKey(); 62 entry.getValue(); 63 } 64 endTime = Calendar.getInstance().getTimeInMillis(); 65 printCostTime(i, mapArray.length, "for iterator entrySet", endTime - startTime); 66 } 67 68 // Type 3 69 for (int i = 0; i < mapArray.length; i++) { 70 Map
map = mapArray[i]; 71 startTime = Calendar.getInstance().getTimeInMillis(); 72 for (String key : map.keySet()) { 73 map.get(key); 74 } 75 endTime = Calendar.getInstance().getTimeInMillis(); 76 printCostTime(i, mapArray.length, "for each keySet", endTime - startTime); 77 } 78 79 // Type 4 80 for (int i = 0; i < mapArray.length; i++) { 81 Map
map = mapArray[i]; 82 startTime = Calendar.getInstance().getTimeInMillis(); 83 Set
> entrySet = map.entrySet(); 84 for (Entry
entry : entrySet) { 85 entry.getKey(); 86 entry.getValue(); 87 } 88 endTime = Calendar.getInstance().getTimeInMillis(); 89 printCostTime(i, mapArray.length, "for entrySet=entrySet()", endTime - startTime); 90 } 91 } 92 93 static int FIRST_COLUMN_LENGTH = 23, OTHER_COLUMN_LENGTH = 12, TOTAL_COLUMN_LENGTH = 71; 94 static final DecimalFormat COMMA_FORMAT = new DecimalFormat("#,###"); 95 96 public static void printHeader(Map... mapArray) { 97 printRowDivider(); 98 for (int i = 0; i < mapArray.length; i++) { 99 if (i == 0) {100 StringBuilder sb = new StringBuilder().append("map size");101 while (sb.length() < FIRST_COLUMN_LENGTH) {102 sb.append(" ");103 }104 System.out.print(sb);105 }106 107 StringBuilder sb = new StringBuilder().append("| ").append(COMMA_FORMAT.format(mapArray[i].size()));108 while (sb.length() < OTHER_COLUMN_LENGTH) {109 sb.append(" ");110 }111 System.out.print(sb);112 }113 TOTAL_COLUMN_LENGTH = FIRST_COLUMN_LENGTH + OTHER_COLUMN_LENGTH * mapArray.length;114 printRowDivider();115 }116 117 public static void printRowDivider() {118 System.out.println();119 StringBuilder sb = new StringBuilder();120 while (sb.length() < TOTAL_COLUMN_LENGTH) {121 sb.append("-");122 }123 System.out.println(sb);124 }125 126 public static void printCostTime(int i, int size, String caseName, long costTime) {127 if (i == 0) {128 StringBuilder sb = new StringBuilder().append(caseName);129 while (sb.length() < FIRST_COLUMN_LENGTH) {130 sb.append(" ");131 }132 System.out.print(sb);133 }134 135 StringBuilder sb = new StringBuilder().append("| ").append(costTime).append(" ms");136 while (sb.length() < OTHER_COLUMN_LENGTH) {137 sb.append(" ");138 }139 System.out.print(sb);140 141 if (i == size - 1) {142 printRowDivider();143 }144 }145 }

 

  1. 测试结果(1,000,000条,3次) 

     除了第三种遍历方式 通过HashMap中的keySet()方法获取key集合,通过循环获取value其余的三种遍历方式所耗时间差距不大。 

        2. 结果分析

    由于其余三种遍历方式耗时差距不大,我们单独拿出与众不同的那一种遍历方式分析,揪出其源代码,找出造成耗时增长的原因。

    相比于其余三种,通过HashMap中的keySet()方法获取key集合,通过循环获取value  (以下统称第三种方式)这种方法使用了keySet()这种方法,那么是不是这个元凶造成了遍历耗时增长呢?

 

    老规矩,先贴源代码。

1 //HashMap entrySet和keySet的源码 2 private final class KeyIterator extends HashIterator
{ 3 public K next() { 4 return nextEntry().getKey(); 5 } 6 } 7 8 private final class EntryIterator extends HashIterator
> { 9 public Map.Entry
next() { 10 return nextEntry(); 11 } 12 }

     以上两种分别返回的是keySet() 和 entrySet()返回的set的迭代器。

     两种方法的区别只是返回值不同,父类相同。理论上讲两种方法的性能应该是相差无几的,在返回值上第三种方式多了一步getKey()的操作。 

     根据key获取value的时间复杂成都根据hash算法而产生差异,源码:

1 public V get(Object key) { 2     if (key == null) 3         return getForNullKey(); 4     Entry
entry = getEntry(key); 5 6 return null == entry ? null : entry.getValue(); 7 } 8 9 /** 10 * Returns the entry associated with the specified key in the 11 * HashMap. Returns null if the HashMap contains no mapping 12 * for the key. 13 */ 14 final Entry
getEntry(Object key) { 15 int hash = (key == null) ? 0 : hash(key); 16 for (Entry
e = table[indexFor(hash, table.length)]; 17 e != null; 18 e = e.next) { 19 Object k; 20 if (e.hash == hash && 21 ((k = e.key) == key || (key != null && key.equals(k)))) 22 return e; 23 } 24 return null; 25 }

   get的时间复杂程度取决于for循环的次数。

 

  3.四种遍历方法的使用总结

     A:单从代码的角度出发:

      如果只需要key值的而不需要value值的话可以使用:

1 Map
map = new HashMap
();2 for (String key : map.keySet()) {3 }

        B:从功能性和性能的角度出发:

       在同时需要key值和value值的前提下,无论是性能还是代码的简洁性来说,通过HashMap中的keySet()方法获取key集合,通过循环获取value  这种方法都是一个比较好的选择。

1 Map
map = new HashMap
();2 for (Entry
entry : map.entrySet()) {3 entry.getKey();4 entry.getValue();5 }
  • 三 利用遍历的方式移除HashMap中的键值对

    我们先来做一个猜想,是否可以通过前面的遍历方式来移除HashMap中的键值对?在上面的代码中加上

  map.remove() ?

    答案是不行的,在运行时会抛出以下异常 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的元素过程中删除了当前所在元素,下一个待访问的元素的指针也由此丢失了。

    所以我们要换一种遍历方式,代码如下:

1 for (Iterator
> it =myHashMap.entrySet().iterator(); it.hasNext();){2 Map.Entry
item = it.next();3 it.remove();4 }5 for (Map.Entry
item : myHashMap.entrySet()){6 System.out.println(item.getKey());7 }

    这种方法能满足大多数情况下的清除键值对的要求,那么对于特殊情况下我们应该怎么解决?

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

            这种情况是我在寻找别人碰到的类似的问题的时候发现的解决办法,所以在这里我就直接照搬了。 (转自@ 

侵删)

    

    如果你的HashMap中的键值同样是一个HashMap,假设你需要处理的是 HashMap<HashMap<String, Integer>, Double> myHashMap 时,很不碰巧,你可能需要修改myHashMap中的一个项的键值HashMap中的某些元素,之后再将其删除。

 

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

 

  例子如下:

1 HashMap
, Integer> myHashMap = new HashMap<>(); 2 HashMap
temp = new HashMap<>(); 3 temp.put("1", 1); 4 temp.put("2", 2); 5 myHashMap.put(temp, 3); 6 for (Iterator
, Integer>> 7 it = myHashMap.entrySet().iterator(); it.hasNext();){ 8 Map.Entry
, Integer> item = it.next(); 9 item.getKey().remove("1");10 System.out.println(myHashMap.size());11 it.remove();12 System.out.println(myHashMap.size());13 }

  结果如下:

11

  

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

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


       解决方案:

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

  想法很好,代码如下:

1 for (Iterator
, Integer>> 2 it = myHashMap.entrySet().iterator(); it.hasNext();){ 3 Map.Entry
, Integer> item = it.next(); 4 //item.getKey().remove("1"); 5 HashMap
to_put = new HashMap<>(item.getKey()); 6 to_put.remove("1"); 7 myHashMap.put(to_put, item.getValue()); 8 System.out.println(myHashMap.size()); 9 it.remove();10 System.out.println(myHashMap.size());11 }

  但是依然是RE:

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

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


 

  

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

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

  运行结果如下:

21

 

转载于:https://www.cnblogs.com/Bloglwu/p/7908232.html

你可能感兴趣的文章
CentOS系统启动流程你懂否
查看>>
Windows Phone 7 添加按钮的VSM状态分组
查看>>
文件夹的隐藏(六)
查看>>
[Unity3d]水果忍者-声音和刀光的实现
查看>>
pthread_mutex_t的静态初始化
查看>>
Redis实战(3)数据类型一Strings
查看>>
接口属性
查看>>
CCNA课堂练习一:路由器链路备份功能
查看>>
LAMP环境下ECShop系统部署
查看>>
在Sony VAIO VGN-UX27CN UMPC上安装Windows XP之经验谈
查看>>
Attach Volume 操作(Part II) - 每天5分钟玩转 OpenStack(54)
查看>>
Windows Phone 7 软件体系结构
查看>>
SLC教程
查看>>
卡巴司机如此重大bug为何视而不改?
查看>>
关于AIX上裸设备表空间管理
查看>>
JavaScript -- 类型转换
查看>>
化零为整WCF(4) - 异常处理(Exception、FaultException、FaultException、IErrorHandler)
查看>>
【Shiro】Shiro从小白到大神(四)-集成Web
查看>>
netstat使用--10个常用的命令
查看>>
XFS设计
查看>>