日常工作中 Map
相对是咱们 Java
程序员高频应用的一种数据结构,那 Map
都有哪些遍历形式呢?这篇文章阿粉就带大家看一下,看看你常常应用的是哪一种。
通过 entrySet
来遍历
1、通过 for
和 map.entrySet()
来遍历
第一种形式是采纳 for
和 Map.Entry
的模式来遍历,通过遍历 map.entrySet()
获取每个 entry
的 key
和 value
,代码如下。这种形式个别也是阿粉应用的比拟多的一种形式,没有什么花里胡哨的用法,就是很奢侈的获取 ma
p 的 key
和 value
。
public static void testMap1(Map<Integer, Integer> map) {
long sum = 0;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {sum += entry.getKey() + entry.getValue();}
System.out.println(sum);
}
看过 HashMap
源码的同学应该会发现,这个遍历形式在源码中也有应用,如下图所示,
putMapEntries
办法在咱们调用 putAll
办法的时候会用到。
2、通过 for
,Iterator
和 map.entrySet()
来遍历
咱们第一个办法是间接通过 for
和 entrySet()
来遍历的,这次咱们应用 entrySet()
的迭代器来遍历,代码如下。
public static void testMap2(Map<Integer, Integer> map) {
long sum = 0;
for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {Map.Entry<Integer, Integer> entry = entries.next();
sum += entry.getKey() + entry.getValue();
}
System.out.println(sum);
}
3、通过 while
,Iterator
和 map.entrySet()
来遍历
下面的迭代器是应用 for
来遍历,那咱们天然能够想到还能够用 while
来进行遍历,所以代码如下所示。
public static void testMap3(Map<Integer, Integer> map) {Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
long sum = 0;
while (it.hasNext()) {Map.Entry<Integer, Integer> entry = it.next();
sum += entry.getKey() + entry.getValue();
}
System.out.println(sum);
}
这种办法跟下面的办法相似,只不过循环从 for
换成了 while
,日常咱们在开发的时候,很多场景都能够将 for
和 while
进行替换。2 和 3 都应用迭代器 Iterator
,通过迭代器的 next()
,办法来获取下一个对象,顺次判断是否有 next
。
通过 keySet 来遍历
下面的这三种形式尽管代码的写法不同,然而都是通过遍历 map.entrySet()
来获取后果的,必由之路。接下来咱们看另外的一组。
4、通过 for 和 map.keySet() 来遍历
后面的遍历是通过 map.entrySet()
来遍历,这里咱们通过 map.keySet()
来遍历,顾名思义前者是保留 entry
的汇合,后者是保留 key
的汇合,遍历的代码如下,因为是 key
的汇合,所以如果想要获取 key
对应的 value
的话,还须要通过 map.get(key)
来获取。
public static void testMap4(Map<Integer, Integer> map) {
long sum = 0;
for (Integer key : map.keySet()) {sum += key + map.get(key);
}
System.out.println(sum);
}
5、通过 for
,Iterator
和 map.keySet()
来遍历
public static void testMap5(Map<Integer, Integer> map) {
long sum = 0;
for (Iterator<Integer> key = map.keySet().iterator(); key.hasNext(); ) {Integer k = key.next();
sum += k + map.get(k);
}
System.out.println(sum);
}
6、通过 while
,Iterator
和 map.keySet()
来遍历
public static void testMap6(Map<Integer, Integer> map) {Iterator<Integer> it = map.keySet().iterator();
long sum = 0;
while (it.hasNext()) {Integer key = it.next();
sum += key + map.get(key);
}
System.out.println(sum);
}
咱们能够看到这种形式绝对于 map.entrySet()
形式,多了一步 get
的操作,这种场景比拟适宜咱们只须要 key
的场景,如果也须要应用 value
的场景不倡议应用 map.keySet()
来进行遍历,因为会多一步 map.get()
的操作。
Java 8 的遍历形式
留神上面的几个遍历办法都是是 JDK 1.8
引入的,如果应用的 JDK
版本不是 1.8
以及之后的版本的话,是不反对的。
7、通过 map.forEach()
来遍历
JDK
中的 forEach
办法,使用率也挺高的。
public static void testMap7(Map<Integer, Integer> map) {final long[] sum = {0};
map.forEach((key, value) -> {sum[0] += key + value;
});
System.out.println(sum[0]);
}
该办法被定义在 java.util.Map#forEach
中,并且是通过 default
关键字来标识的,如下图所示。这里提个问题,为什么要应用 default
来标识呢?欢送把你的答案写在评论区。
8、Stream
遍历
public static void testMap8(Map<Integer, Integer> map) {long sum = map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum();
System.out.println(sum);
}
9、ParallelStream
遍历
public static void testMap9(Map<Integer, Integer> map) {long sum = map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum();
System.out.println(sum);
}
这两种遍历形式都是 JDK 8
的 Stream
遍历形式,stream
是一般的遍历,parallelStream
是并行流遍历,在某些场景会晋升性能,然而也不肯定。
测试代码
下面的遍历形式有了,那么咱们在日常开发中到底该应用哪一种呢?每一种的性能是怎么样的呢?为此阿粉这边通过上面的代码,咱们来测试一下每种形式的执行工夫。
public static void main(String[] args) {
int outSize = 1;
int mapSize = 200;
Map<Integer, Integer> map = new HashMap<>(mapSize);
for (int i = 0; i < mapSize; i++) {map.put(i, i);
}
System.out.println("---------------start------------------");
long totalTime = 0;
for (int size = outSize; size > 0; size--) {long startTime = System.currentTimeMillis();
testMap1(map);
totalTime += System.currentTimeMillis() - startTime;}
System.out.println("testMap1 avg time is :" + (totalTime / outSize));
// 省略其余办法,代码跟下面统一
}
为了防止一些烦扰,这里通过外层的 for
来进行屡次计算,而后求平均值,当咱们的参数别离是 outSize
= 1,mapSize
= 200 的时候,测试的后果如下
当随着咱们增大 mapSize
的时候,咱们会发现,前面几个办法的性能是逐步回升的。
总结
从下面的例子来看,当咱们的汇合数量很少的时候,基本上一般的遍历就能够搞定,不须要应用 JDK 8
的高级 API
来进行遍历,当咱们的汇合数量较大的时候,就能够思考采纳 JDK 8
的 forEach
或者 Stream
来进行遍历,这样的话效率更高。在一般的遍历办法中 entrySet()
的办法要比应用 keySet()
的办法好。
本文由 mdnice 多平台公布