一、for循环 + KeySet(效率低)/values
分别遍历 key 和 values
通过map.keySet()方法可以获取Map的所有key的集合(set)。然后通过遍历这个Set就可以遍历到Map的key,如果想要同时遍历到Map的value,则需要进一步通过key来从Map这个集合中获取对应的value。【不推荐,只能获取key或value,要想获取对应的value和key,需要重复计算】
for(String key:map.keySet()){
System.out.println(key);
}
for(String value:map.values()){
System.out.println(value);
}
二、get方法获取;Iterator + Keyset(效率低)
通过Set<K> keySet() 方法获取Map集合所有的key,然后通过key获取value
Set<String> keySet = map.keySet();
for (String key:keySet){
System.out.println(key + ":" + map.get(key));
}
或
Set set = map.keySet();
Iterator<String> it = set.iterator();
while (it.hasNext()){
String key = it.next();
System.out.println("Key:" + key + ";" + "Value:" + map.get(key));
}
相对来说通过Set<K> keySet() 方法效率更低,获取Map集合所有的key,然后通过key获取value,需要遍历Key获取值,本身就耗时
注:
根据阿里开发手册,也不建议使用这种方法,因为会迭代两次。keySet获取Iterator一次,还要通过get又迭代一次,降低性能
三、Entryset
for循环 + Entryset;Iterator迭代器迭代 + Entryset
for(Map.Entry<Integer,String> entry : map.entrySet()){
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
首先通过map.entrySet()方法,可以获取到一个Set集合,这个集合中的每一个元素就是Map中的一个键值对。然后通过循环遍历这个Set集合,可以依次取出每对的键和值。该方法使用了foreach循环,代码简洁明了,且能获取Map的键和值,是最常见且多数情况最可取的遍历方式
注:for-each循环在java 5中被引入。所以该方法只能应用于java 5及以上版本。如果遍历Map是null,for-each循环将抛出NullPointerException,因此在遍历前需要检查空引用
通过Set<Map.Entry<K,V>> entrySet()方法获取泛型对象为 Map.Entry<K,V> 的Set集合,然后使用Iterator迭代器遍历,每次取出 Map.Entry<K,V> 对象的 key和value
Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
Map.Entry<String, String> entry = entries.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+":"+value);
}
Entry是Map接口的内部接口,获取迭代器,然后循环依次取出每个迭代器里面的Entry,再通过Entry取出每个键值对。该种方法看起来冗余,却是老版本java中遍历map的唯一方式。第一种遍历方法其实是这种方法通过语法糖做的一种编码方式,其本质是一样的。所以在性能方面,两种方法基本相同。另外,如果在遍历过程中,有删除某些键值对的需求,需要使用这种遍历方式
四、Java8中Map接口中默认Lambda表达式
从Java8开始,Java提供了对Lambda表达式的支持,通过Lambda表达式可以使代码更简洁,这其中就包括用Lambda表达式实现遍历Map的功能
/**
*
* default void forEach(BiConsumer<? super K, ? super V> action)
* BiConsumer 接口中方法:
* void accept(T t, U u); 对给定的参数执行操作
*/
map.forEach((key,value) -> {
System.out.println(key + ":" + value);
});
Lambda表达式的forEach方法,其实就是一种语法糖,可以让代码更加简洁,使用更加方便。查看源码,我们可以发现,这种方式也是对entrySet遍历方式的一种包装
五、Stream流
map.entrySet().stream().forEach((Map.Entry<Integer, String> entry) -> {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});
总结:
1、EntrySet的方式比KeySet性能要好,原因在于keySet相当于遍历了2次
2、单纯的获取key来说,两者的差别并不大,但是如果要获取value,还是entrySet的效率会更好,因为keySet需要从map中再次根据key获取value,而entrySet一次都全部获取出来
map.get(key)是计算密集型操作,很耗费CPU
3、EntrySet的Iterator遍历的效率比for循环效率更好
4、lambda 表达式和Entryset的底层原理相同,且语法更加简洁。
结论:JAVA8以下推荐使用方法三,即for循环 + Entryset;Iterator迭代器迭代 + Entryset
JAVA8及以上,推荐使用方法四、五,即Lambda表达式和Stream
注:
foreach的语法只是对iterator进行了简单的包装,使用起来更加方便而已,但是如果在foreach循环体内,对集合元素进行删除添加操作的时候,会报出ConcurrentModificationException,并报修改异常。如果需要在遍历集合的时候对象集合中元素进行删除操作,需要使用iterator的遍历方式,iterator自带的remove删除方式不会报出异常