牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了


牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
我们知道java HashMap的扩容是有成本的 , 为了减少扩容的次数和成本 , 可以给HashMap设置初始容量大小 , 如下所示:
但是在实际使用的过程中 , 发现性能不但没有提升 , 反而显著下降了!代码里对HashMap的操作也只有遍历了 , 看来是遍历出了问题 , 于是做了一番测试 , 得到如下结果:
迭代器测试
贴上测试代码:
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
这是运行结果:
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
我们将第一个Map初始化10w大小 , 第二个map不指定大小(实际16) , 两个存储相同的数据 , 但是用迭代器遍历100次的时候发现性能差异 , 一个36ms一个4ms , 实际上性能差距更大 , 这里的4ms是600次System.out.print的耗时 , 这里将print注掉再试下
输出结果如下:
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
可以发现第二个map耗时几乎为0 , 第一个达到了28ms , 遍历期间没有进行任何操作 , 既然石锤了和 initial capacity 有关 , 下一步我们去看看为什么会这样 , 找找Map迭代器的源码看看 。
迭代器源码探究
我们来看看Map.entrySet.iterator的源码;
其中EntryIterator是HashMap的内部抽象类 , 源码并不多 , 我全部贴上来并附上中文注释
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
上面的代码一看就明白了 , 迭代器每次寻找下一个元素都会去遍历数组 , 如果 initial capacity 特别大的话 , 也就是说 threshold 也大 , table.length就大 , 所以遍历比较耗性能 。
table数组的大小设置是在resize方法里:
其他遍历方法
注意代码里我们用的是Map.entrySet.iterator , 实际上和keys.iterator, values.iterator 一样 , 源码如下:
牛皮了!头一次见大佬把「Map遍历性能问题」详解得如此清晰明了
本文插图
这两个就不分析了 , 性能一样 。
实际使用中对集合的遍历还有几种方法:
增强型for循环
增强行for循环实际上是通过迭代器来实现的 , 我们来看两者的联系