乐趣区

关于后端:Java-中九种-Map-的遍历方式你一般用的是哪种呢

日常工作中 Map 相对是咱们 Java 程序员高频应用的一种数据结构,那 Map 都有哪些遍历形式呢?这篇文章阿粉就带大家看一下,看看你常常应用的是哪一种。

通过 entrySet 来遍历

1、通过 formap.entrySet() 来遍历

第一种形式是采纳 forMap.Entry 的模式来遍历,通过遍历 map.entrySet() 获取每个 entrykeyvalue,代码如下。这种形式个别也是阿粉应用的比拟多的一种形式,没有什么花里胡哨的用法,就是很奢侈的获取 map 的 keyvalue

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、通过 forIteratormap.entrySet() 来遍历

咱们第一个办法是间接通过 forentrySet() 来遍历的,这次咱们应用 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、通过 whileIteratormap.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,日常咱们在开发的时候,很多场景都能够将 forwhile 进行替换。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、通过 forIteratormap.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、通过 whileIteratormap.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 8Stream 遍历形式,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 8forEach 或者 Stream 来进行遍历,这样的话效率更高。在一般的遍历办法中 entrySet() 的办法要比应用 keySet() 的办法好。

本文由 mdnice 多平台公布

退出移动版