Map遍历方式效率分析
文章目录
- 概要
- 本地环境
- 测试数据
- Map遍历方式
- keySet的Iterator遍历(key、value)
- 遍历key
- 遍历value
- 遍历key+value
- keySet的for遍历(key、value)
- 遍历key
- 遍历value
- 遍历key+value
- entrySet的Iterator遍历(key、value)
- 遍历key
- 遍历value
- 遍历key+value
- entrySet的for遍历(key、value)
- 遍历key
- 遍历value
- 遍历key+value
- values的Iterator遍历(value)
- values的for遍历(value)
- 测试工具
- 数据运行结果
- 总结
概要
项目中我们经常会用到Map,也不可避免的需要遍历Map。偶然间刷到一篇文章,不推荐用keySet()遍历HashMap,推荐使用entrySet()进行遍历。给出的理由是keySet()遍历需要经历两次遍历,一次是转为Iterator对象,另一次是是从HashMap中取出key所对应的value;而entrySet()遍历只需要经历一次遍历。这里就不再分析底层的运行逻辑,而是以简单直观的数据运行来展示各遍历方式的效率。
本地环境
处理器:12th Gen Intel® Core™ i5-12500 3.00 GHz
机带RAM:32.0 GB
IDE:Eclipse 2024-09 (4.33.0)
JDK:jdk1.8.0_152
测试数据
准备两对数据(interval分别为1和100):
第一队:1开始,每次增加1,即1,2,3,…
第二队:100开始,每次增加100,即100,200,300,…
准备五组对照组(size分别为1000、10000、100000、1000000和10000000)
数据获取代码如下:
public static Map<String, String> getNumberMap(int size, int interval) {Map<String, String> map = new HashMap<String, String>();String key, value;for (int i = 1; i <= size; i++) {key = "K" + i * interval;value = "V" + i * interval;map.put(key, value);}return map;}
Map遍历方式
keySet的Iterator遍历(key、value)
遍历key
public static void keySetIterator4K(Map<String, String> map) {Iterator<String> iterator = map.keySet().iterator();String key;while (iterator.hasNext()) {key = iterator.next();}}
遍历value
public static void keySetIterator4V(Map<String, String> map) {Iterator<String> iterator = map.keySet().iterator();String value;while (iterator.hasNext()) {value = map.get(iterator.next());}}
遍历key+value
public static void keySetIterator4KV(Map<String, String> map) {Iterator<String> iterator = map.keySet().iterator();String key, value;while (iterator.hasNext()) {key = iterator.next();value = map.get(key);}}
keySet的for遍历(key、value)
遍历key
public static void keySetFor4K(Map<String, String> map) {for (String key : map.keySet()) {}}
遍历value
public static void keySetFor4V(Map<String, String> map) {String value;for (String key : map.keySet()) {value = map.get(key);}}
遍历key+value
public static void keySetFor4KV(Map<String, String> map) {String value;for (String key : map.keySet()) {value = map.get(key);}}
entrySet的Iterator遍历(key、value)
遍历key
public static void entrySetIterator4K(Map<String, String> map) {Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();String key;while (iterator.hasNext()) {key = iterator.next().getKey();}}
遍历value
public static void entrySetIterator4V(Map<String, String> map) {Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();String value;while (iterator.hasNext()) {value = iterator.next().getValue();}}
遍历key+value
public static void entrySetIterator4KV(Map<String, String> map) {Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();Map.Entry<String, String> entry;String key, value;while (iterator.hasNext()) {entry = iterator.next();key = entry.getKey();value = entry.getValue();}}
entrySet的for遍历(key、value)
遍历key
public static void entrySetFor4K(Map<String, String> map) {String key;for (Map.Entry<String, String> entry : map.entrySet()) {key = entry.getKey();}}
遍历value
public static void entrySetFor4V(Map<String, String> map) {String value;for (Map.Entry<String, String> entry : map.entrySet()) {value = entry.getValue();}}
遍历key+value
public static void entrySetFor4KV(Map<String, String> map) {String key, value;for (Map.Entry<String, String> entry : map.entrySet()) {key = entry.getKey();value = entry.getValue();}}
values的Iterator遍历(value)
public static void valuesIterator4V(Map<String, String> map) {Iterator<String> iterator = map.values().iterator();String value;while (iterator.hasNext()) {value = iterator.next();}}
values的for遍历(value)
public static void valuesFor4V(Map<String, String> map) {for (String value : map.values()) {}}
测试工具
public static void main(String[] args) {int count = 1; // 测试执行次数int[] size = {1000, 10000, 100000, 1000000, 10000000}; // 数据量int[] interval = {1, 100}; // 初始及增长long startTime, endTime;for (int i = 1; i <= count; i++) {System.out.println("第" + i + "次测试:");for (int j = 0; j < size.length; j++) {System.out.println(" size=" + size[j] + ":");for (int k = 0; k < interval.length; k++) {StringBuffer result = new StringBuffer();Map<String, String> map = getNumberMap(size[j], interval[k]);/*** 遍历key*/startTime = System.currentTimeMillis();keySetIterator4K(map); // keySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();keySetFor4K(map); // keySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetIterator4K(map); // entrySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetFor4K(map); // entrySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);/*** 遍历value*/startTime = System.currentTimeMillis();keySetIterator4V(map); // keySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();keySetFor4V(map); // keySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetIterator4V(map); // entrySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetFor4V(map); // entrySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();valuesIterator4V(map); // values的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();valuesFor4V(map); // values的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);/*** 遍历key+value*/startTime = System.currentTimeMillis();keySetIterator4KV(map); // keySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();keySetFor4KV(map); // keySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetIterator4KV(map); // entrySet的Iterator遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);getResultTime(startTime, endTime, result);startTime = System.currentTimeMillis();entrySetFor4KV(map); // entrySet的for遍历endTime = System.currentTimeMillis();getResultTime(startTime, endTime, result);System.out.println(" " + result.toString());}}}}
public static void getResultTime(long startTime, long endTime, StringBuffer result) {String time = String.valueOf(endTime - startTime);if (result.toString().isEmpty()) {result.append(time);} else {result.append(" 、" + time);}}
数据运行结果
Map遍历方式效率分析 | |||||||||||
遍历场景 | 遍历方式 | 测试数据耗时(ms) | |||||||||
数据量1000 | 数据量10000 | 数据量100000 | 数据量1000000 | 数据量10000000 | |||||||
数据1 | 数据100 | 数据1 | 数据100 | 数据1 | 数据100 | 数据1 | 数据100 | 数据1 | 数据100 | ||
key | keySet的Iterator遍历 | 1 | 0 | 1 | 1 | 5 | 4 | 16 | 27 | 138 | 167 |
keySet的for遍历 | 0 | 0 | 1 | 1 | 3 | 3 | 14 | 25 | 148 | 166 | |
entrySet的Iterator遍历 | 1 | 0 | 2 | 1 | 5 | 4 | 16 | 27 | 153 | 179 | |
entrySet的for遍历 | 0 | 0 | 0 | 1 | 4 | 3 | 15 | 26 | 156 | 177 | |
value | keySet的Iterator遍历 | 0 | 0 | 1 | 0 | 5 | 7 | 19 | 36 | 216 | 212 |
keySet的for遍历 | 0 | 0 | 1 | 1 | 4 | 6 | 20 | 36 | 222 | 215 | |
entrySet的Iterator遍历 | 0 | 1 | 0 | 1 | 4 | 3 | 16 | 26 | 156 | 172 | |
entrySet的for遍历 | 0 | 0 | 0 | 0 | 4 | 4 | 15 | 26 | 160 | 175 | |
values的Iterator遍历 | 1 | 0 | 1 | 1 | 4 | 3 | 15 | 25 | 150 | 162 | |
values的for遍历 | 0 | 0 | 0 | 0 | 4 | 3 | 14 | 25 | 148 | 162 | |
key+value | keySet的Iterator遍历 | 0 | 0 | 1 | 0 | 5 | 5 | 20 | 37 | 210 | 214 |
keySet的for遍历 | 0 | 0 | 1 | 0 | 5 | 5 | 19 | 37 | 213 | 210 | |
entrySet的Iterator遍历 | 0 | 0 | 1 | 0 | 5 | 4 | 16 | 27 | 164 | 178 | |
entrySet的for遍历 | 1 | 0 | 1 | 0 | 4 | 3 | 17 | 28 | 168 | 177 |
总结
对比上面表格做出以下总结:
1、当Map的size在100000及以下时,各种遍历方式之间差别不大
2、当Map的size为100000,字符串长度最大为9时,多数遍历方式出现字符串长度大遍历效率高(?)
3、当Map的size为100w以上时,可以明显看出keySet的Iterator遍历和for遍历在遍历value时耗时高于其他遍历方式
4、当Map的size为1000w,字符串长度最大为11时,字符串长度对遍历效率的影响变小(?)
所以,遍历HashMap时,如果只是遍历key或者数据量小于10000(?)时,那么无论哪种方式都是可以使用的;如果涉及到value的遍历,那么尽量避免使用keySet()相关的遍历。