本文已收录《面试精选》系列,Gitee开源地址:https://gitee.com/mydb/interviewHashMap的遍历方法有很多,不同的JDK版本有不同的写法,其中JDK8提供了3种HashMap的遍历方式,一举打破了以往遍历方式“非常臃肿”的尴尬。1、JDK8之前的遍历JDK8之前主要使用EntrySet和KeySet进行遍历。具体实现代码如下。1.1EntrySet遍历EntrySet是早期HashMap遍历的主要方法,其实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapmap=newHashMap(){{put("Java","Java值。");put("MySQL","MySQL值");put("Redis","Redis值");}};//遍历for(Map.Entryentry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:1.2KeySet遍历KeySet的方式是循环Key内容,然后通过map.get(key)获取Value的值,具体实现是如下:publicstaticvoidmain(String[]args){//创建并分配一个hashmapHashMapmap=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值");put("Redis","Redis值");}};//遍历for(Stringkey:map.keySet()){System.out.println(key+":"+map.get(key));}}上面程序的执行结果如下图所示:KeySet性能问题通过上面的代码我们可以看出,使用KeySet进行遍历,其性能不如EntrySet,因为KeySet实际上循环了两次集合,首先一个循环就是循环Key,获取Value需要用map.get(key),相当于循环一次集合,所以不推荐KeySet循环,循环后效率比较低两次1.3EntrySet迭代器遍历EntrySet和KeySet除了上面的直接循环,我们还可以使用它们的迭代器来循环。例如EntrySet迭代器的实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapmap=newHashMap(){{put("Java","Java值。");put("MySQL","MySQL值");put("Redis","Redis值。");}};//遍历Iterator>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entryentry=iterator.next();System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:1.4KeySet迭代器遍历KeySet也可以用迭代器遍历,实现代码如下:publicstaticvoidmain(String[]args){//创建和assignhashmapHashMapmap=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值");put("Redis","Redis值");}};//遍历Iteratoriterator=map.keySet().iterator();while(iterator.hasNext()){Stringkey=iterator.next();System.out.println(key+":"+map.get(key));}}上面程序的执行结果如下图所示注意:虽然不推荐使用KeySet循环方式,但是还是有必要了解1.5迭代器的作用。既然可以直接遍历,为什么还要用迭代器呢?我们通过下面的例子就知道了。不使用迭代器删除如果不使用迭代器,如果我们在遍历EntrySet时删除遍历代码中的元素,代码的实现如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMap<字符串,字符串>map=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值");put("Redis","Redis值");}};//遍历for(Map.Entryentry:map.entrySet()){if("Java".equals(entry.getKey())){//删除此项map.remove(entry.获取密钥());继续;}System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:可以看到,如果在遍历代码中动态删除元素,在非迭代器方法中会报错。使用迭代器删除接下来,我们使用迭代器循环EntrySet,在循环中动态删除元素,实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapmap=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值");put("Redis","Redis值");}};//循环遍历Iterator>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entryentry=iterator.next();if("Java".equals(entry.getKey())){//删除此项iterator.remove();继续;}System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:从上面的结果我们可以看出使用迭代器的好处是可以在循环过程中动态删除集合中的元素。上面的非迭代器方法不能在循环中删除元素(程序会报错)。2、JDK8之后的遍历JDK8之后,HashMap的遍历变得方便多了。JDK8包括以下三种遍历方式:使用Lambda遍历,使用Stream单线程遍历,使用Stream多线程遍历,我们分别来看。2.1Lambda遍历使用Lambda表达式遍历方法的实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值一个hashmapHashMapmap=newHashMap(){{put(“Java”,“Java值。”);put("MySQL","MySQL值");put("Redis","Redis值");}};//循环map.forEach((key,value)->{System.out.println(key+":"+value);});}上面程序的执行结果如下图所示:2.2Stream单线程遍历Stream遍历是先获取map集合的EntrySet,然后执行forEach循环,实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapmap=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值");put("Redis","Redis值");}};//循环遍历map.entrySet().stream().forEach((entry)->{System.out.println(entry.getKey()+":"+entry.getValue());});}上述程序的执行结果如下图所示:2.3Streammultithreaded遍历Streammultithreaded遍历方式与前面的遍历方式类似,只是执行的是并行并发执行方式。该方法会根据当前硬件配置生成相应数量的线程,然后进行遍历操作实现代码如下:publicstaticvoidmain(String[]args){//创建并分配hashmapHashMapmap=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQL值。");put("Redis","Redis值");}};//遍历map.entrySet().stream().parallel().forEach((entry)->{System.out.println(entry.getKey()+":"+entry.getValue());});}上面程序的执行结果如下图所示:注意上图的执行结果,可以看到当前的执行结果和之前所有的遍历结果都不一样(打印元素的顺序不一样)),因为程序是并发执行的,所以没办法保证元素的执行顺序和打印顺序,这是并发编程的特点。推荐哪一个?遍历方法?不同的场景推荐不同的遍历方式。比如如果是JDK8之后的开发环境,推荐使用Stream遍历的方式,因为足够简单;而如果在遍历过程中需要动态删除元素,那么推荐使用迭代器遍历方式;如果你在遍历的时候更关心程序的执行效率,那么推荐使用Stream多线程遍历方式,因为它足够快。因此,这个问题的答案是不固定的。我们需要知道每种遍历方式的优缺点,然后根据不同的场景灵活运用。小结本文介绍了7种HashMap遍历方法。JDK8之前主要使用EntrySet和KeySet遍历方式,而KeySet遍历方式性能比较低,一般不推荐使用。不过在JDK8之后,遍历方式有了新的选择。可以使用更简单的Lambda遍历,或者性能更高的Stream多线程遍历。判断是非在己,名誉在人,得失在数。博主介绍:一位80后程序员,“坚持”了11年的博客。爱好:阅读、慢跑和羽毛球。公众号:Java面试真题解析