我注意到在实施时有些奇怪HashMap.clear()。这就是在OpenJDK 7u40中的样子:
public void clear() { modCount++; Arrays.fill(table, null); size = 0; } 这就是从OpenJDK 8u40开始的样子:
public void clear() { Node<K,V>[] tab; modCount++; if ((tab = table) != null && size > 0) { size = 0; for (int i = 0; i < tab.length; ++i) tab[i] = null; } } 我知道现在table可以将null用作空映射,因此需要在局部变量中进行额外的检查和缓存。但是,为什么Arrays.fill()要用for循环代替?
似乎此提交中引入了更改。不幸的是,我没有找到解释为什么普通的for循环会比更好Arrays.fill()。它更快吗?或更安全? 问题来源于stack overflow
在我看来这个更改做了两个更新: 1. 对table做了非空判定, 减少了报空指针 2. 用for代替是减少了一次类的引用, jdk8以后采用了懒加载, 如果一个类的静态成员没有使用的话,是不加载的,没用的类也不会加载. 这里一改, 如果别处没有用arrays,那么arrays就不会加载,加快了运行速度
因为它是多快!
我对这两种方法的简化版本进行了一些彻底的基准测试:
void jdk7clear() { Arrays.fill(table, null); }
void jdk8clear() { Object[] tab; if ((tab = table) != null) { for (int i = 0; i < tab.length; ++i) tab[i] = null; } } 对包含随机值的各种大小的数组进行运算。以下是(典型)结果:
Map size | JDK 7 (sd)| JDK 8 (sd)| JDK 8 vs 7 16| 2267 (36)| 1521 (22)| 67% 64| 3781 (63)| 1434 ( 8)| 38% 256| 3092 (72)| 1620 (24)| 52% 1024| 4009 (38)| 2182 (19)| 54% 4096| 8622 (11)| 4732 (26)| 55% 16384| 27478 ( 7)| 12186 ( 8)| 44% 65536| 104587 ( 9)| 46158 ( 6)| 44% 262144| 445302 ( 7)| 183970 ( 8)| 41% 这是在填充了空值的数组上进行操作时的结果(因此,消除了垃圾收集问题):
Map size | JDK 7 (sd)| JDK 8 (sd)| JDK 8 vs 7 16| 75 (15)| 65 (10)| 87% 64| 116 (34)| 90 (15)| 78% 256| 246 (36)| 191 (20)| 78% 1024| 751 (40)| 562 (20)| 75% 4096| 2857 (44)| 2105 (21)| 74% 16384| 13086 (51)| 8837 (19)| 68% 65536| 52940 (53)| 36080 (16)| 68% 262144| 225727 (48)| 155981 (12)| 69% 数字以纳秒为单位,(sd)是表示为结果百分比的1个标准偏差(fyi,“正态分布”总体的SD为68),vs是相对于JDK 7的JDK 8计时。
有趣的是,它不仅速度明显加快,而且偏差也稍窄,这意味着JDK 8实现提供了更加一致的性能。
该测试在jdk 1.8.0_45上对填充有随机Integer对象的阵列进行了多次(百万次)测试。为了删除偏远的数字,在每组结果中,最快和最慢的3%计时被丢弃。请求垃圾回收,并且在每次调用该方法之前就退出并休眠线程。在前20%的工作中就完成了JVM预热,这些结果被丢弃了。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。