最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中使用HashMap時(shí)指定初始化容量性能解析

 更新時(shí)間:2023年02月05日 10:50:55   作者:xindoo  
這篇文章主要為大家介紹了Java中使用HashMap時(shí)指定初始化容量性能解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

一些Java編程老手在做CodeReview時(shí),都會(huì)告訴其他人,使用HashMap時(shí)建議指定容量大小,原因是指定容量后,代碼性能會(huì)更好一些。后來(lái)隨著阿里Java開(kāi)發(fā)手冊(cè)在業(yè)內(nèi)廣為傳播,這一點(diǎn)早已深入人心,我自己也早已習(xí)慣在使用HashMap時(shí)指定容量大小。

但我今天突發(fā)奇想,想知道指定容量和不指定容量時(shí)性能究竟有多少的差異,測(cè)試部分測(cè)試數(shù)據(jù)的結(jié)果讓我大跌眼睛,有些情況下指定容量的性能還比不指定容量時(shí)差??! ,但其他部分還是很符合我之前的認(rèn)知的。

openjdk17和jmh單線程測(cè)試

先說(shuō)下我的測(cè)試平臺(tái)和測(cè)試方法,我使用了openjdk17和jmh單線程測(cè)試,測(cè)試代碼如下:

    @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中小數(shù)據(jù)緩存,我特意使用了隨機(jī)數(shù)作為KEY,而VALUE一視同仁都使用了1。兩個(gè)方法就是新建一個(gè)HashMap并不斷往map里put數(shù)據(jù),唯一差異就是一個(gè)指定了CAP參數(shù)。 在我設(shè)置了不同參數(shù)后,得到了以下數(shù)據(jù)(越高越好):

數(shù)據(jù)量不指定容量(ops/s)指定容量(ops/s)
25109543324000032
42516175611813275
8107671765900641
1629783742987958
3212316371545394
64567643764260
256129350185540
10242747535799
10252719568466
409666819937
327688071177
65536377567

可以看出,容量16是個(gè)分水嶺,當(dāng)容量為16時(shí),二者幾乎沒(méi)啥差異,這也很容易理解,當(dāng)不指定容量時(shí)默認(rèn)初始容量就是16。

  • 但容量大于16時(shí),指定容量時(shí)的性能會(huì)高于不指定時(shí)的性能,隨著數(shù)量的增加,前者會(huì)比后者性能高出50%。
  • 但當(dāng)數(shù)據(jù)量小于16時(shí),不指定容量大小反而性能更高,最多甚至相差2倍,這就和我們之前的認(rèn)知不一樣了。

上面數(shù)據(jù)中還有個(gè)很奇怪的點(diǎn),那就是當(dāng)數(shù)據(jù)量為1025時(shí),性能居然還高于1024,而且差異巨大。就好比別人比你多干了1份活,但用的時(shí)間比你少一半。我跑了多次都是這個(gè)結(jié)果,這不是測(cè)試誤差,這個(gè)結(jié)果和計(jì)算機(jī)底層存儲(chǔ)實(shí)現(xiàn)有關(guān),具體原理可以參考問(wèn)題 為什么轉(zhuǎn)置512x512的矩陣比轉(zhuǎn)置513x513的矩陣慢?

備注:以上數(shù)據(jù)經(jīng)過(guò)多次運(yùn)行測(cè)試,數(shù)據(jù)雖有波動(dòng),但數(shù)據(jù)波動(dòng)基本都在3%以內(nèi)。

那為什么在大數(shù)據(jù)量的情況下,指定容量的代碼性能會(huì)更好呢?這就得說(shuō)到HashMap的實(shí)現(xiàn)原理,更詳細(xì)內(nèi)容可以參考我之前寫(xiě)的HashMap源碼淺析。這里為了方便大家直觀地理解性能差異產(chǎn)生的原因,我們用牧場(chǎng)養(yǎng)羊類比下。

假設(shè)你要開(kāi)始養(yǎng)羊,你得現(xiàn)有場(chǎng)地吧,假設(shè)你先找了塊小場(chǎng)地,但隨著你的羊群發(fā)展壯大,場(chǎng)地不夠用了,你就得搬到一個(gè)更大的新場(chǎng)地,如果發(fā)展速度特別快,你就得頻繁搬家,搬家就逐漸變成了負(fù)擔(dān)。但如果你一開(kāi)始就知道你最多能養(yǎng)多少的羊,直接找個(gè)足夠大的場(chǎng)地,不就能省去一直搬家的成本了嗎!

這里你把羊類比成數(shù)據(jù),場(chǎng)地類比為內(nèi)存,在HashMap中,如果開(kāi)始不指定容量大小,JVM默認(rèn)會(huì)給你一個(gè)非常小的(16)的容量空間,如果之后數(shù)據(jù)量變多,就需要重新申請(qǐng)更大的空間,并把數(shù)據(jù)遷移到新空間上,于是額外增加了時(shí)間消耗。這便是性能差異產(chǎn)生的原因。

但當(dāng)容量小于16時(shí),指定容量的方式反而性能更差。這個(gè)我之前從未看過(guò)其他資料有說(shuō)過(guò),我簡(jiǎn)單談下自己的分析和理解。 當(dāng)調(diào)用new HashMap()和new HashMap(CAP)時(shí),分別執(zhí)行了不同的構(gòu)造函數(shù),而二者的構(gòu)造函數(shù)的邏輯是有差異的,當(dāng)指定容量時(shí),執(zhí)行了容量參數(shù)檢查的代碼:

    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;
    }

不指定容量時(shí),構(gòu)造方法內(nèi)只有一行this.loadFactor = DEFAULT_LOAD_FACTOR;,在put的數(shù)據(jù)量一致時(shí),后續(xù)所有的代碼執(zhí)行流程都是一致的,所以指定容量時(shí),上面容量參數(shù)檢查的代碼帶來(lái)了額外的性能負(fù)擔(dān),所以導(dǎo)致數(shù)據(jù)量較小時(shí)指定容量時(shí)反而性能更差一些。

總結(jié)

最后回到文章標(biāo)題上來(lái),Java中使用HashMap時(shí)指定初始化容量性能一定會(huì)更好嘛?答案是不一定,指定容量也有可能性能會(huì)更差。當(dāng)然,絕大多數(shù)情況下還是建議指定容量的,類似的還有ArrayList,也建議指定容量。 別人給出的結(jié)論不一定的完全正確的,只有知道產(chǎn)生結(jié)論的原因,才能更有效的利用這個(gè)結(jié)論。

以上就是Java中使用HashMap時(shí)指定初始化容量性能解析的詳細(xì)內(nèi)容,更多關(guān)于Java HashMap容量性能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Java解析XML的四種方法

    詳解Java解析XML的四種方法

    本篇文章主要介紹了java解析XML的幾種方式,XML現(xiàn)在已經(jīng)成為一種通用的數(shù)據(jù)交換格式,給數(shù)據(jù)集成與交互提供了方便,有需要的可以了解一下。
    2016-11-11
  • java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和

    java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和

    這篇文章主要給大家介紹了關(guān)于java?stream實(shí)現(xiàn)分組BigDecimal求和以及自定義分組求和的相關(guān)資料,Stream是Java8的一大亮點(diǎn),是對(duì)容器對(duì)象功能的增強(qiáng),它專注于對(duì)容器對(duì)象進(jìn)行各種非常便利、高效的聚合操作或者大批量數(shù)據(jù)操作,需要的朋友可以參考下
    2023-12-12
  • 學(xué)習(xí)java一定要知道的垃圾收集器

    學(xué)習(xí)java一定要知道的垃圾收集器

    這篇文章主要介紹了學(xué)習(xí)java一定要知道的垃圾收集器,垃圾收集器的發(fā)展路線,簡(jiǎn)單來(lái)說(shuō)是隨著內(nèi)存越來(lái)越大而發(fā)生變化,更多相關(guān)介紹需要的朋友可以參考一下
    2022-07-07
  • Springboot處理異常的常見(jiàn)方式

    Springboot處理異常的常見(jiàn)方式

    SpringBoot框架異常處理有多種處理方式,今天就帶大家了解一下常見(jiàn)的springboot異常處理方式,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • MyBatis中的ResultMap的association和collection標(biāo)簽詳解

    MyBatis中的ResultMap的association和collection標(biāo)簽詳解

    這篇文章主要介紹了MyBatis中的ResultMap的association和collection標(biāo)簽詳解,主要包括association標(biāo)簽常用參數(shù)及id & result標(biāo)簽參數(shù)詳解,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • SpringBoot連接MYSQL數(shù)據(jù)庫(kù)并使用JPA進(jìn)行操作

    SpringBoot連接MYSQL數(shù)據(jù)庫(kù)并使用JPA進(jìn)行操作

    今天給大家介紹一下如何SpringBoot中連接Mysql數(shù)據(jù)庫(kù),并使用JPA進(jìn)行數(shù)據(jù)庫(kù)的相關(guān)操作。
    2017-04-04
  • SpringBoot2.x 整合 thumbnailator 圖片處理的示例代碼

    SpringBoot2.x 整合 thumbnailator 圖片處理的示例代碼

    這篇文章主要介紹了SpringBoot2.x 之整合 thumbnailator 圖片處理,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 面試初級(jí)Java開(kāi)發(fā)問(wèn)到Arrays

    面試初級(jí)Java開(kāi)發(fā)問(wèn)到Arrays

    這篇文章主要介紹了Java Arrays工具類用法,結(jié)合實(shí)例形式分析了java Arrays工具類針對(duì)數(shù)組元素修改、復(fù)制、排序等操作使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2021-07-07
  • Java?Handler同步屏障淺析講解

    Java?Handler同步屏障淺析講解

    同步屏障機(jī)制是什么?Handler發(fā)送的消息分為普通消息、屏障消息、異步消息,一旦Looper在處理消息時(shí)遇到屏障消息,那么就不再處理普通的消息,而僅僅處理異步的消息。不再使用屏障后,需要撤銷屏障,不然就再也執(zhí)行不到普通消息了
    2022-08-08
  • SpringBoot2整合JTA組件實(shí)現(xiàn)多數(shù)據(jù)源事務(wù)管理

    SpringBoot2整合JTA組件實(shí)現(xiàn)多數(shù)據(jù)源事務(wù)管理

    這篇文章主要介紹了SpringBoot2整合JTA組件實(shí)現(xiàn)多數(shù)據(jù)源事務(wù)管理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03

最新評(píng)論

合阳县| 灵台县| 桃源县| 吐鲁番市| 信丰县| 从江县| 深泽县| 鄂托克前旗| 庆阳市| 台江县| 合山市| 栾川县| 海阳市| 荥阳市| 姜堰市| 蒙城县| 徐汇区| 阿坝| 米泉市| 达孜县| 屏山县| 池州市| 甘谷县| 福贡县| 溆浦县| 北票市| 名山县| 元朗区| 石门县| 太保市| 宜章县| 图片| 屏南县| 平陆县| 翁源县| 平罗县| 文昌市| 台北市| 锡林郭勒盟| 武胜县| 油尖旺区|