HashMap的循环姿势你真的使用对了吗?

开发 后端
hashMap 应该是java程序员工作中用的比较多的一个键值对处理的数据的类型了。

[[342610]]

hashMap 应该是java程序员工作中用的比较多的一个键值对处理的数据的类型了。hashMap 有常见的六七种遍历的方式。这么多的选择,大家平时都是使用哪一种来遍历数据列?欢迎大家在下方留言哦。说实话这么多种方式,想记也不记不住,也不想浪费时间来记这玩意,所以本人在JDK1.8以前基本上都是用Map.Entry的方式来遍历,1.8及以后就习惯性用forEach了,不过这个不能有continue或者break操作这个有时候还是挺不方便的,其他几种基本上没怎么用过,也没太研究这几种方式,哪种性能是比较好的。反正就是挑自己熟悉的方式。好了话不多说,我们还是直入今天的主题。先来看看每种遍历的方式:

在for循环中使用entries实现Map的遍历

  1. public static void forEachEntries() { 
  2.         for (Map.Entry<String, String> entry : map.entrySet()) { 
  3.             String mapKey = entry.getKey(); 
  4.             String mapValue = entry.getValue(); 
  5.         } 
  6.     } 

在for循环中遍历key

  1. public static void forEachKey() { 
  2.         for (String key : map.keySet()) { 
  3.             String mapKey = key
  4.             String mapValue = map.get(mapKey); 
  5.         } 
  6.     } 

在for循环中遍历value

  1. public static void forEachValues() { 
  2.         for (String key : map.values()) { 
  3.             String val = key
  4.         } 
  5.     } 

Iterator遍历

  1. public static void forEachIterator() { 
  2.         Iterator<Entry<String, String>> entries = map.entrySet().iterator(); 
  3.         while (entries.hasNext()) { 
  4.             Entry<String, String> entry = entries.next(); 
  5.             String key = entry.getKey(); 
  6.             String value = entry.getValue(); 
  7.         } 
  8.     } 

forEach jdk1.8遍历

  1. public static void forEach() { 
  2.         map.forEach((key, val) -> { 
  3.             String key1 = key
  4.             String value = val; 
  5.         }); 
  6.     } 

Stream jdk1.8遍历

  1. map.entrySet().stream().forEach((entry) -> { 
  2.             String key = entry.getKey(); 
  3.             String value = entry.getValue(); 
  4.         }); 

Streamparallel jdk1.8遍历

  1. public static void forEachStreamparallel() { 
  2.         map.entrySet().parallelStream().forEach((entry) -> { 
  3.             String key = entry.getKey(); 
  4.             String value = entry.getValue(); 
  5.         }); 
  6.     } 

以上就是常见的对于map的一些遍历的方式,下面我们来写个测试用例来看下这些遍历方式,哪些是效率最好的。下面测试用例是基于JMH来测试的 首先引入pom

  1. <dependency> 
  2.             <groupId>org.openjdk.jmh</groupId> 
  3.             <artifactId>jmh-core</artifactId> 
  4.             <version>1.23</version> 
  5.         </dependency> 
  6.         <dependency> 
  7.             <groupId>org.openjdk.jmh</groupId> 
  8.             <artifactId>jmh-generator-annprocess</artifactId> 
  9.             <version>1.23</version> 
  10.             <scope>provided</scope> 
  11.         </dependency> 

关于jmh测试如可能会影响结果的一些因素这里就不详细介绍了,可以参考文末的第一个链接写的非常详细。以及测试用例为什么要这么写(都是为了消除JIT对测试代码的影响)这是参照官网的链接:编写测试代码如下:

  1. package com.workit.autoconfigure.autoconfigure.controller; 
  2.  
  3.  
  4. import org.openjdk.jmh.annotations.*; 
  5. import org.openjdk.jmh.infra.Blackhole; 
  6. import org.openjdk.jmh.results.format.ResultFormatType; 
  7. import org.openjdk.jmh.runner.Runner; 
  8. import org.openjdk.jmh.runner.RunnerException; 
  9. import org.openjdk.jmh.runner.options.Options; 
  10. import org.openjdk.jmh.runner.options.OptionsBuilder; 
  11.  
  12. import java.util.HashMap; 
  13. import java.util.Iterator; 
  14. import java.util.Map; 
  15. import java.util.Map.Entry; 
  16. import java.util.UUID; 
  17. import java.util.concurrent.TimeUnit; 
  18.  
  19. /** 
  20.  * @author:公众号:java金融 
  21.  * @Date:  
  22.  * @Description:微信搜一搜【java金融】回复666 
  23.  */ 
  24.  
  25. @State(Scope.Thread) 
  26. @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 
  27. @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) 
  28. @Fork(1) 
  29. @BenchmarkMode(Mode.AverageTime) 
  30. @OutputTimeUnit(TimeUnit.NANOSECONDS) 
  31. public class InstructionsBenchmark { 
  32.     public static void main(String[] args) throws RunnerException { 
  33.         Options opt = new OptionsBuilder().include(InstructionsBenchmark.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build(); 
  34.         new Runner(opt).run(); 
  35.     } 
  36.  
  37.     static final int BASE = 42; 
  38.  
  39.     static int add(int key,int val) { 
  40.       return  BASE + key +val; 
  41.     } 
  42.     @Param({"1""10""100""1000","10000","100000"}) 
  43.     int size
  44.     private static Map<IntegerInteger>  map; 
  45.  
  46.     // 初始化方法,在全部Benchmark运行之前进行 
  47.     @Setup(Level.Trial) 
  48.     public void init() { 
  49.         map = new HashMap<>(size); 
  50.         for (int i = 0; i < size; i++) { 
  51.             map.put(i, i); 
  52.         } 
  53.     } 
  54.  
  55.  
  56.     /** 
  57.      * 在for循环中使用entries实现Map的遍历: 
  58.      */ 
  59.     @Benchmark 
  60.     public static void forEachEntries(Blackhole blackhole) { 
  61.         for (Map.Entry<IntegerInteger> entry : map.entrySet()) { 
  62.             Integer mapKey = entry.getKey(); 
  63.             Integer mapValue = entry.getValue(); 
  64.             blackhole.consume(add(mapKey,mapValue)); 
  65.         } 
  66.     } 
  67.  
  68.     /** 
  69.      * 在for循环中遍历key 
  70.      */ 
  71.     @Benchmark 
  72.     public static StringBuffer forEachKey(Blackhole blackhole) { 
  73.         StringBuffer stringBuffer = new StringBuffer(); 
  74.         for (Integer key : map.keySet()) { 
  75.           //  Integer mapValue = map.get(key); 
  76.             blackhole.consume(add(key,key)); 
  77.         } 
  78.         return stringBuffer; 
  79.     } 
  80.  
  81.     /** 
  82.      * 在for循环中遍历value 
  83.      */ 
  84.     @Benchmark 
  85.     public static void forEachValues(Blackhole blackhole) { 
  86.         for (Integer key : map.values()) { 
  87.             blackhole.consume(add(key,key)); 
  88.         } 
  89.     } 
  90.  
  91.     /** 
  92.      * Iterator遍历; 
  93.      */ 
  94.     @Benchmark 
  95.     public static void forEachIterator(Blackhole blackhole) { 
  96.         Iterator<Entry<IntegerInteger>> entries = map.entrySet().iterator(); 
  97.         while (entries.hasNext()) { 
  98.             Entry<IntegerInteger> entry = entries.next(); 
  99.             Integer key = entry.getKey(); 
  100.             Integer value = entry.getValue(); 
  101.             blackhole.consume(add(key,value)); 
  102.         } 
  103.     } 
  104.  
  105.     /** 
  106.      * forEach jdk1.8遍历 
  107.      */ 
  108.     @Benchmark 
  109.     public static void forEachLamada(Blackhole blackhole) { 
  110.         map.forEach((key, value) -> { 
  111.             blackhole.consume(add(key,value)); 
  112.         }); 
  113.  
  114.     } 
  115.  
  116.     /** 
  117.      * forEach jdk1.8遍历 
  118.      */ 
  119.     @Benchmark 
  120.     public static void forEachStream(Blackhole blackhole) { 
  121.         map.entrySet().stream().forEach((entry) -> { 
  122.             Integer key = entry.getKey(); 
  123.             Integer value = entry.getValue(); 
  124.             blackhole.consume(add(key,value)); 
  125.  
  126.         }); 
  127.     } 
  128.  
  129.     @Benchmark 
  130.     public static void forEachStreamparallel(Blackhole blackhole) { 
  131.         map.entrySet().parallelStream().forEach((entry) -> { 
  132.             Integer key = entry.getKey(); 
  133.             Integer value = entry.getValue(); 
  134.             blackhole.consume(add(key,value)); 
  135.  
  136.         }); 
  137.     } 
  138.  

运行结果如下:「注:运行环境idea 2019.3,jdk1.8,windows7 64位。」

  1. Benchmark                                    (size)  Mode  Cnt        Score        Error  Units 
  2. InstructionsBenchmark.forEachEntries              1  avgt    5       10.021 ±      0.224  ns/op 
  3. InstructionsBenchmark.forEachEntries             10  avgt    5       71.709 ±      2.537  ns/op 
  4. InstructionsBenchmark.forEachEntries            100  avgt    5      738.873 ±     12.132  ns/op 
  5. InstructionsBenchmark.forEachEntries           1000  avgt    5     7804.431 ±    136.635  ns/op 
  6. InstructionsBenchmark.forEachEntries          10000  avgt    5    88540.345 ±  14915.682  ns/op 
  7. InstructionsBenchmark.forEachEntries         100000  avgt    5  1083347.001 ± 136865.960  ns/op 
  8. InstructionsBenchmark.forEachIterator             1  avgt    5       10.675 ±      2.532  ns/op 
  9. InstructionsBenchmark.forEachIterator            10  avgt    5       73.934 ±      4.517  ns/op 
  10. InstructionsBenchmark.forEachIterator           100  avgt    5      775.847 ±    198.806  ns/op 
  11. InstructionsBenchmark.forEachIterator          1000  avgt    5     8905.041 ±   1294.618  ns/op 
  12. InstructionsBenchmark.forEachIterator         10000  avgt    5    98686.478 ±  10944.570  ns/op 
  13. InstructionsBenchmark.forEachIterator        100000  avgt    5  1045309.216 ±  36957.608  ns/op 
  14. InstructionsBenchmark.forEachKey                  1  avgt    5       18.478 ±      1.344  ns/op 
  15. InstructionsBenchmark.forEachKey                 10  avgt    5       76.398 ±     12.179  ns/op 
  16. InstructionsBenchmark.forEachKey                100  avgt    5      768.507 ±     23.892  ns/op 
  17. InstructionsBenchmark.forEachKey               1000  avgt    5    11117.896 ±   1665.021  ns/op 
  18. InstructionsBenchmark.forEachKey              10000  avgt    5    84871.880 ±  12056.592  ns/op 
  19. InstructionsBenchmark.forEachKey             100000  avgt    5  1114948.566 ±  65582.709  ns/op 
  20. InstructionsBenchmark.forEachLamada               1  avgt    5        9.444 ±      0.607  ns/op 
  21. InstructionsBenchmark.forEachLamada              10  avgt    5       76.125 ±      5.640  ns/op 
  22. InstructionsBenchmark.forEachLamada             100  avgt    5      861.601 ±     98.045  ns/op 
  23. InstructionsBenchmark.forEachLamada            1000  avgt    5     7769.714 ±   1663.914  ns/op 
  24. InstructionsBenchmark.forEachLamada           10000  avgt    5    73250.238 ±   6032.161  ns/op 
  25. InstructionsBenchmark.forEachLamada          100000  avgt    5   836781.987 ±  72125.745  ns/op 
  26. InstructionsBenchmark.forEachStream               1  avgt    5       29.113 ±      3.275  ns/op 
  27. InstructionsBenchmark.forEachStream              10  avgt    5      117.951 ±     13.755  ns/op 
  28. InstructionsBenchmark.forEachStream             100  avgt    5     1064.767 ±     66.869  ns/op 
  29. InstructionsBenchmark.forEachStream            1000  avgt    5     9969.549 ±    342.483  ns/op 
  30. InstructionsBenchmark.forEachStream           10000  avgt    5    93154.061 ±   7638.122  ns/op 
  31. InstructionsBenchmark.forEachStream          100000  avgt    5  1113961.590 ± 218662.668  ns/op 
  32. InstructionsBenchmark.forEachStreamparallel       1  avgt    5       65.466 ±      5.519  ns/op 
  33. InstructionsBenchmark.forEachStreamparallel      10  avgt    5     2298.999 ±    721.455  ns/op 
  34. InstructionsBenchmark.forEachStreamparallel     100  avgt    5     8270.759 ±   1801.082  ns/op 
  35. InstructionsBenchmark.forEachStreamparallel    1000  avgt    5    16049.564 ±   1972.856  ns/op 
  36. InstructionsBenchmark.forEachStreamparallel   10000  avgt    5    69230.849 ±  12169.260  ns/op 
  37. InstructionsBenchmark.forEachStreamparallel  100000  avgt    5   638129.559 ±  14885.962  ns/op 
  38. InstructionsBenchmark.forEachValues               1  avgt    5        9.743 ±      2.770  ns/op 
  39. InstructionsBenchmark.forEachValues              10  avgt    5       70.761 ±     16.574  ns/op 
  40. InstructionsBenchmark.forEachValues             100  avgt    5      745.069 ±    329.548  ns/op 
  41. InstructionsBenchmark.forEachValues            1000  avgt    5     7772.584 ±   1702.295  ns/op 
  42. InstructionsBenchmark.forEachValues           10000  avgt    5    74063.468 ±  23752.678  ns/op 
  43. InstructionsBenchmark.forEachValues          100000  avgt    5   994057.370 ± 279310.867  ns/op 

 

通过上述的图我们可以发现,数据量较小的时候forEachEntries和forEachIterator、以及lamada循环效率都差不多forEachStreamarallel的效率反而较低,只有当数据量达到10000以上parallelStream的优势就体现出来了。所以平时选择使用哪种循环方式的时候没必要太纠结哪一种方式,其实每种方式之间的效率还是微乎其微的。选择适合自己的就好。为什么parallelStream在数据量较小的时候效率反而不行?这个大家可以在下方留言哦。

总结

 

上面小实验只是在我机器上跑出来的结果,可能放到不同的机器运行结果有不一样哦,大家感兴趣的同学可以把代码贴到自己的机器上跑一跑,也许我这这个结论就不适用了。

本文转载自微信公众号「 java金融」,可以通过以下二维码关注。转载本文请联系 java金融公众号。

 

责任编辑:武晓燕 来源: java金融
相关推荐

2022-05-09 07:27:50

ThreadLocaJava

2022-03-11 14:59:21

JavaScript数组字符串

2018-07-01 08:34:09

缓存数据服务

2017-11-09 13:56:46

数据库MongoDB水平扩展

2024-12-17 15:00:00

字符串Java

2021-03-16 06:47:47

Python

2013-07-15 16:55:45

2019-03-08 16:30:44

MySQPHP数据库

2019-12-26 14:07:19

随机数伪随机多线程

2024-09-18 10:08:37

2019-05-28 11:52:43

可视化图表数据

2020-08-04 08:37:23

Kafka分区数

2020-06-29 08:32:21

高并发程序员流量

2018-03-19 10:39:28

Java序列化对象

2024-01-25 10:14:09

HashSetHashMapJava

2015-01-26 10:55:56

云服务器PowerEdge C

2022-01-12 18:35:54

MongoDB数据查询

2022-04-07 08:20:22

typeinterface前端

2018-01-25 16:49:08

开源容器云编排工具
点赞
收藏

51CTO技术栈公众号