遍历Map的六种方式

一.创建Map

遍历需要先创建一个测试类,这个测试类我们简单的写成如下

HashMap<String,String> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
map.put("key4","value4");

二.遍历Map

方式一:使用entrySet遍历(推荐使用)

遍历方式如下:这里可以选择使用for循环、增强for循环、do-while循环、lamdba表达式等方式对EntrySet实现遍历,这些本质上都是遍历entrySet的不同方式而已,都属于同一种方式遍历,这里只展示使用lamdba的遍历方式。

Set<Map.Entry<String,String>> set = map.entrySet();
set.stream().forEach(entry ->{
    System.out.println(entry.getKey()+":"+entry.getValue());
});

方式二:使用keySet遍历

这里也可以选择使用for循环、增强for循环、do-while循环、lamdba表达式等方式对KeySet实现遍历,这些本质上都是遍历keySet的同一种方式遍历,这里只展示使用lamdba的遍历方式。

Set<String> set2 = map.keySet();
set2.stream().forEach(key ->{
    System.out.println(key+":"+map.get(key));
});

方式三:使用entrySet+Iterator遍历

这种方式需要使用entrySet方法加上迭代器,代码如下:

Iterator<Map.Entry<String,String>> iterable =  map.entrySet().iterator();
while(iterable.hasNext()){
     Map.Entry<String,String> entry = iterable.next();
     System.out.println(entry.getKey()+":"+entry.getValue());
 }

方式四:使用keySet+Iterator遍历

这种方式需要使用keySet方法加上迭代器,代码如下:

Iterator<String> set3 = map.keySet().iterator();
while(set3.hasNext()){
    String key = set3.next();
    System.out.println(key+":"+map.get(key));
}

方式五:使用keySet、values方法遍历

这种方式是key和value分别遍历的形式,应用场景不多,但也是一种方式,且效率不高,不推荐使用

Set<String> set4 = map.keySet();
set4.stream().forEach(key ->{
    System.out.println(key);
});
Collection<String> values = map.values();
values.stream().forEach(value ->{
    System.out.println(value);
});

方式六:使用JDK8提供的forEach方法遍历(推荐使用)

forEach方法是JDK8提供的方法,是为了我们方便使用lamdba。这个方法使用lamdba来实现,是非常简洁的。需要传入一个BiConsumer<K,U>,k和u其实就是键和值,我们只需要传入然后操作即可,代码如下:

map.forEach((key,value) ->{
    System.out.println(key+":"+value);
});

三.总结

六种方式我们不可能都用到,肯定还是需要选取效率最高的来使用,那哪种效率最高呢,来实际测试下。

1.测试六种遍历的效率

笔者往HashMap中插入了100万个数据,然后测试了这六种不同遍历方式所需要的时间如,且每个方式都经过了多次测试,得出数据如下(单位:毫秒):

//数组长度值的来源:1000000/0.75+1=1333334
HashMap<String,String> hashMap = new HashMap<>(1333334);

方式一:3500-4000之间
方式二:3500-4000之间
方式三:3000-3600之间
方式四:3500-4000之间
方式五:6000-7000之间
方式六:3500-4000之间

可以发现除了key、value分别遍历的场景耗时比较严重,其他没有什么差别,显然这个结果是不对的,那是什么原因影响了正常结果呢,显然不可能是数据量不够,那么就只可能是即时编译器的锅了,我们知道即时编译器会对代码优化以及指令重排,这会影响到我们的正常程序逻辑。下面笔者禁用了即时编译器又重新测试了下。

2.禁用即时编译器,重新测试遍历的效率

首先我们禁用即时编译器,如下:
遍历Map的六种方式-小白菜博客
然后再一次测试各个遍历方式所消耗的时间,这里展示第一种遍历耗费时间的截图:
遍历Map的六种方式-小白菜博客
经过多次测试,六种遍历方式所耗时间如下:

方式一:稳定在8100左右
方式二:稳定在9900左右
方式三:稳定在8900左右
方式四:稳定在12000左右
方式五:稳定在12000左右
方式六:稳定在8300左右

上面所示就是六种遍历HashMap的效率了,可以清晰的看到哪个遍历效率才是最高的,排除微弱因素影响,我们使用方式一和方式六肯定是最好的选择。下面根据效率队各种遍历进行一个排序。

方式六(使用forEach方法)
等价于
方式一(使用entrySet方法)
优于
方式三(使用entrySet方法+Iterator迭代器)
优于
方式二(使用keySet方法)
优于
方式四(使用keySet方法+Iterator迭代器)
优于
方式五(使用keySet、values方法,分别遍历key、value)

3.该如何选择HashMap的遍历方式

根据上面的测试结果,可以很清晰的看到,我们在开发中应该首选forEach方法进行对HashMap进行遍历,使用entrySet方法也是可以的,但是不要使用keySet对Map进行遍历,所有涉及keySet的遍历方式效率均不高。阿里的开发手册中也是明令禁止使用keySet方法对HashMap进行遍历的。这里对HashMap的遍历总结就完了,希望这个总结也可以帮到路过的你。