当前位置: 首页 > 科技观察

面试官:HashMap有多少遍历方法?推荐哪个?

时间:2023-03-20 21:11:37 科技观察

HashMap有多种遍历方式,不同的JDK版本有不同的写法。其中,JDK8提供了三种HashMap遍历方法,打破了以往遍历方法“非常臃肿”的尴尬。1、JDK8之前的遍历JDK8之前主要使用EntrySet和KeySet进行遍历。具体实现代码如下。1.1EntrySet遍历EntrySet是早期HashMap遍历的主要方法,其实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//遍历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","MySQLValue.");put("Redis","RedisValue.");}};//循环通过for(Stringkey:map.keySet()){System.out.println(key+":"+map.get(key));}}上面程序的执行结果如图下图:KeySet的性能问题通过上面的代码我们可以看出,使用KeySet来遍历,其性能是不如EntrySet的,因为KeySet其实循环了两次集合,第一次循环是循环Key,然后得到值,你需要使用map.get(key),相当于循环遍历一次集合,所以不推荐使用KeySet循环,因为循环了两次,效率比较低1.3EntrySet迭代器遍历EntrySet和KeySet除了上面的直接循环,我们还可以使用它们的迭代器来循环。例如EntrySet迭代器的实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//循环遍历Iterator>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entryentry=iterator.next();System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:1.4KeySetIteratorIterator也可以用来遍历KeySet来遍历,实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//遍历Iteratoriterator=map.keySet().iterator();while(iterator.hasNext()){Stringkey=iterator.next();System.out.println(key+":"+map.get(key));}}上面程序的执行结果如下图所示:虽然不推荐使用KeySet循环方式,但是还是有必要了解一下1.5迭代器的作用既然可以直接遍历,为什么还要用迭代器呢?我们通过下面的例子就知道了。不使用迭代器删除如果不使用迭代器,如果我们在遍历EntrySet时删除遍历代码中的元素,代码的实现如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//循环for(Map.Entryentry:map.entrySet()){if("Java".equals(entry.getKey())){//删除此项map.remove(entry.getKey());continue;}System.out.println(entry.getKey()+":"+entry.getValue());}}上面程序的执行结果如下图所示:可以看到如果动态到删除一个元素,在非迭代器方法中会报错。使用迭代器删除接下来,我们使用迭代器循环EntrySet,在循环中动态删除元素,实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//遍历Iterator>iterator=map.entrySet().iterator();while(iterator.hasNext()){Map.Entryentry=iterator.next();if("Java".equals(entry.getKey())){//删除这个iterator.remove();continue;}System.out.println(entry.getKey()+":"+entry.getValue());}}上面的程序执行结果如下图所示:从上面的结果可以看出,使用迭代器的好处是可以在循环过程中动态删除集合中的元素。上面的非迭代器方法不能在循环中删除元素(程序会报错)。2、JDK8之后的遍历JDK8之后,HashMap的遍历变得方便多了。JDK8包括以下三种遍历方式:Lambda遍历Stream遍历单线程遍历Stream多线程遍历我们分别来看。2.1Lambda遍历使用Lambda表达式的遍历方法实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//遍历map.forEach((key,value)->{System.out.println(key+":"+value);});}上面程序的执行结果如下图所示:2.2Stream单线程遍历Stream遍历是先获取map集合的EntrySet,然后执行forEach循环。实现代码如下:publicstaticvoidmain(String[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//循环map.entrySet().stream().forEach((entry)->{System.out.println(entry.getKey()+":"+entry.getValue());});}上面程序的执行结果如下图所示:图2.3Stream多线程遍历Stream多线程遍历方法为和前面的遍历方法类似,只不过多了一个parallel并发执行的方法,这个方法会根据当前硬件配置生成相应数量的线程,然后执行遍历操作,实现代码如下:publicstaticvoidmain(字符串[]args){//创建并赋值hashmapHashMapma??p=newHashMap(){{put("Java","JavaValue.");put("MySQL","MySQLValue.");put("Redis","RedisValue.");}};//遍历map.entrySet().stream().parallel().forEach((entry)->{System.out.println(entry.getKey()+":"+entry.getValue());});}上面程序的执行结果如下图所示:注意上图的执行结果,可以看出当前的执行结果与之前的所有遍历结果(打印元素顺序不同),因为程序是并发执行的,所以没办法保证元素的执行顺序和打印顺序,这是并发编程的特点。推荐哪种遍历方法?不同场景推荐的遍历方式不同,比如如果是JDK8之后的开发环境,推荐使用Stream遍历方式,足够简洁;而如果在遍历过程中需要动态删除元素,那么推荐使用iterator遍历方式;如果遍历,比较在意程序的执行效率的话,建议使用Stream多线程遍历方式,因为速度足够快。因此,这个问题的答案是不固定的。我们需要知道每种遍历方式的优缺点,然后根据不同的场景灵活运用。小结本文介绍了7种HashMap遍历方法。JDK8之前主要使用EntrySet和KeySet遍历方式,而KeySet遍历方式性能比较低,一般不推荐使用。不过在JDK8之后,遍历方式有了新的选择。可以使用更简单的Lambda遍历,或者性能更高的Stream多线程遍历。本文转载自微信公众号《Java面试题解析》,可通过以下二维码关注。转载本文请联系Java面试真题解析公众号。