当前位置:主页 > java教程 > Java HashMap容量性能

Java中使用HashMap时指定初始化容量性能解析

发布:2023-04-13 08:55:01 59


给大家整理一篇相关的编程文章,网友向雅艳根据主题投稿了本篇教程内容,涉及到Java、HashMap容量性能、Java、HashMap容量初始化、Java HashMap容量性能相关内容,已被309网友关注,涉猎到的知识点内容可以在下方电子书获得。

Java HashMap容量性能

正文

一些Java编程老手在做CodeReview时,都会告诉其他人,使用HashMap时建议指定容量大小,原因是指定容量后,代码性能会更好一些。后来随着阿里Java开发手册在业内广为传播,这一点早已深入人心,我自己也早已习惯在使用HashMap时指定容量大小。

但我今天突发奇想,想知道指定容量和不指定容量时性能究竟有多少的差异,测试部分测试数据的结果让我大跌眼睛,有些情况下指定容量的性能还比不指定容量时差!! ,但其他部分还是很符合我之前的认知的。

openjdk17和jmh单线程测试

先说下我的测试平台和测试方法,我使用了openjdk17和jmh单线程测试,测试代码如下:

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @Measurement(iterations = 2, time = 5)
    @Threads(1)
    @Fork(0)
    @Warmup(iterations = 1, time = 5)
    public void withoutCap() {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < CAP; i++) {
            map.put(random.nextInt(), 1);
        }
    }
    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @Measurement(iterations = 2, time = 5)
    @Threads(1)
    @Fork(0)
    @Warmup(iterations = 1, time = 5)
    public void withCap() {
        Map<Integer, Integer> map = new HashMap<>(CAP);
        for (int i = 0; i < CAP; i++) {
            map.put(random.nextInt(), 1);
        }
    }

这里为了避免Java中小数据缓存,我特意使用了随机数作为KEY,而VALUE一视同仁都使用了1。两个方法就是新建一个HashMap并不断往map里put数据,唯一差异就是一个指定了CAP参数。 在我设置了不同参数后,得到了以下数据(越高越好):

数据量不指定容量(ops/s)指定容量(ops/s)
25109543324000032
42516175611813275
8107671765900641
1629783742987958
3212316371545394
64567643764260
256129350185540
10242747535799
10252719568466
409666819937
327688071177
65536377567

可以看出,容量16是个分水岭,当容量为16时,二者几乎没啥差异,这也很容易理解,当不指定容量时默认初始容量就是16。

  • 但容量大于16时,指定容量时的性能会高于不指定时的性能,随着数量的增加,前者会比后者性能高出50%。
  • 但当数据量小于16时,不指定容量大小反而性能更高,最多甚至相差2倍,这就和我们之前的认知不一样了。

上面数据中还有个很奇怪的点,那就是当数据量为1025时,性能居然还高于1024,而且差异巨大。就好比别人比你多干了1份活,但用的时间比你少一半。我跑了多次都是这个结果,这不是测试误差,这个结果和计算机底层存储实现有关,具体原理可以参考问题 为什么转置512x512的矩阵比转置513x513的矩阵慢?

备注:以上数据经过多次运行测试,数据虽有波动,但数据波动基本都在3%以内。

那为什么在大数据量的情况下,指定容量的代码性能会更好呢?这就得说到HashMap的实现原理,更详细内容可以参考我之前写的HashMap源码浅析。这里为了方便大家直观地理解性能差异产生的原因,我们用牧场养羊类比下。

假设你要开始养羊,你得现有场地吧,假设你先找了块小场地,但随着你的羊群发展壮大,场地不够用了,你就得搬到一个更大的新场地,如果发展速度特别快,你就得频繁搬家,搬家就逐渐变成了负担。但如果你一开始就知道你最多能养多少的羊,直接找个足够大的场地,不就能省去一直搬家的成本了吗!

这里你把羊类比成数据,场地类比为内存,在HashMap中,如果开始不指定容量大小,JVM默认会给你一个非常小的(16)的容量空间,如果之后数据量变多,就需要重新申请更大的空间,并把数据迁移到新空间上,于是额外增加了时间消耗。这便是性能差异产生的原因。

但当容量小于16时,指定容量的方式反而性能更差。这个我之前从未看过其他资料有说过,我简单谈下自己的分析和理解。 当调用new HashMap()和new HashMap(CAP)时,分别执行了不同的构造函数,而二者的构造函数的逻辑是有差异的,当指定容量时,执行了容量参数检查的代码:

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +                    loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
    static final int tableSizeFor(int cap) {
        int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

不指定容量时,构造方法内只有一行this.loadFactor = DEFAULT_LOAD_FACTOR;,在put的数据量一致时,后续所有的代码执行流程都是一致的,所以指定容量时,上面容量参数检查的代码带来了额外的性能负担,所以导致数据量较小时指定容量时反而性能更差一些。

总结

最后回到文章标题上来,Java中使用HashMap时指定初始化容量性能一定会更好嘛?答案是不一定,指定容量也有可能性能会更差。当然,绝大多数情况下还是建议指定容量的,类似的还有ArrayList,也建议指定容量。 别人给出的结论不一定的完全正确的,只有知道产生结论的原因,才能更有效的利用这个结论。

以上就是Java中使用HashMap时指定初始化容量性能解析的详细内容,更多关于Java HashMap容量性能的资料请关注码农之家其它相关文章!


参考资料

相关文章

  • Java多线程实战之交叉打印的两种方法

    发布:2022-10-21

    给网友们整理关于Java的教程,今天小编就为大家分享一篇关于Java多线程实战之交叉打印的两种方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧


  • RateLimit使用guava来做接口限流代码示例

    发布:2021-04-29

    这篇文章主要介绍了RateLimit-使用guava来做接口限流代码示例,具有一定借鉴价值,需要的朋友可以参考下


  • Java实例化一个抽象类对象的教程

    发布:2020-01-29

    大家都知道抽象类无法实例化,就无法创建对象。所以下面这篇文章主要给大家介绍了关于Java实例化一个抽象类对象的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴


  • 实例详解hadoop中实现java网络爬虫

    发布:2020-03-22

    下面小编就为大家带来一篇hadoop中实现java网络爬虫(示例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧


  • 总结Java中String.format()的使用

    发布:2020-03-01

    这篇文章主要给大家介绍了如何快速入门介绍Java中强大的String.format()的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着


  • Java-String类最全汇总(下篇)

    发布:2023-03-11

    这篇文章主要介绍了Java-String类最全汇总(下篇),本文章内容详细,本模块分为了两部分,本次为下篇,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧


  • java把string保存到txt文档实例方法

    发布:2020-05-21

    今天小编就为大家分享一篇java 实现将一个string保存到txt文档中的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧


  • 使用java -jar修改SpringBoot中application.properties的配置项

    发布:2023-04-02

    这篇文章主要介绍了使用java -jar修改SpringBoot中application.properties的配置项问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教


网友讨论