前言:
最近,在一次代码评审会议上,有说HashMap遍历要尽量用Iterator,性能好。但是一问性能能提升多少,他也不知道。
在网上搜了一下有没有人做了类似的测试,结果发现了这篇文章《HashMap 的 7 种遍历方式与性能分析!(强烈推荐)》,里面的测试结果说到:几种常用的遍历性能几乎没有任何差别。

我自己也用文章里面的代码本地跑了一下,结果确实是和作者的差不多,因为机器不同,所以看相对值和比例就可以了。

但是这里结论是有问题的。一个问题是这ops/ms应该数字越大性能越好,并行循环这个应该是性能更差的。 平时大家应该都用不到这方法(反正我是没用过),可以先别在意这个点。
再来看一眼测试用的代码:
确实是都将key,value获取出来了,但是都打印出来了,那println对遍历kv的影响有多大呢?输出控制台不仅可能会有锁操作,还有io操作,怎么想这部分的耗时应该都比内存遍历HashMap多吧。

那重新优化一下原文的
Benchmark代码,顺便改了下测试函数的名字,删除了并行循环那个测试。代码如下:这里测试使用fun1将key,和value做个相加返回是为了减少编译优化对获取key,value的干扰,模拟实际业务要用到kv的场景。
测试的运行环境:
跑出来的测试结果如下,并没有像原文一样,一点差距都没有。可以看到,在这个获取kv的场景,
entrySet 是比keySet 再通过key获取value性能要高点的,这也符合我们正常的逻辑,毕竟一步将key,value都获取出来应该是要比两步获取快一点的。但是从结果来看,两者的差距并没有到一个数量级的程度,且可以看到Map.forEach 的遍历速度是最快的。
而遍历到底是用while还是for循环,显不显视
iterator ,问题都不大,甚至IDEA的提示都会让你修改。

到最后,其实就3个遍历写法
这里再引出一个问题,如果不不需要通过Map.get获取value值,效率是不是就一样了呢?
可以再增加几个用例来看下跑出来的结果。
跑出来的测试结果如下
可以看到,如果只是遍历key或value,使用
keySet 或values 是没有问题的,性能没有什么区别(测试结果上的一点数值区别,得把fun1函数里面的加法逻辑算进去)。HashMap的大小会不会对遍历方式的效率有不一样的影响呢?比如说可能当size大于10w后,用Map.each 遍历就没有比EntrySet遍历快了?
然后又跑了个测试,结果如下。可以看到,几种遍历HashMap的方式,不会因为HashMap的大小增长某种遍历方式突然性能提升。总的来说,还是先推荐使用Map.foreach。当然,要是没有追求极致性能的场景,其实用哪个遍历方式都可以,性能差距并没有达到一个数量级的程度。
最后,我们来看下阿里p3c编程规约里面是怎么说的,原文在这:https://github.com/alibaba/p3c/blob/master/p3c-gitbook/编程规约/集合处理.md

也是和本次测试验证的情况吻合。
📎 参考资料
- 作者:Yibin
- 链接:https://yibin.dev/article/cbda2570-6da4-4b16-9f56-126160cd5976
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章








