欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中WeakHashMap和HashMap的區(qū)別詳解

 更新時(shí)間:2023年09月07日 10:04:52   作者:HelloWorld_EE  
這篇文章主要介紹了Java中WeakHashMap和HashMap的區(qū)別詳解,WeakHashMap和HashMap一樣,WeakHashMap也是一個(gè)散列表,它存儲(chǔ)的內(nèi)容也是鍵值對(duì)(key-value)映射,而且鍵和值都可以為null,需要的朋友可以參考下

WeakHashMap和HashMap的區(qū)別

前面對(duì)HashMap的源碼和WeakHashMap的源碼分別進(jìn)行了分析。在WeakHashMap源碼分析博文中有對(duì)與HashMap區(qū)別的比較,但是不夠具體系統(tǒng)。加上本人看了一些相關(guān)的博文,發(fā)現(xiàn)了一些好的例子來(lái)說(shuō)明這兩者的區(qū)別,因此,就有了這篇博文。

WeakHashMap和HashMap一樣,WeakHashMap也是一個(gè)散列表,它存儲(chǔ)的內(nèi)容也是鍵值對(duì)(key-value)映射,而且鍵和值都可以為null。

不過(guò)WeakHashMap的鍵是“弱鍵”(注:源碼中Entry中的定義是這樣的:

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>

即Entry實(shí)現(xiàn)了WeakReference類),當(dāng)WeakHashMap某個(gè)鍵不再正常使用時(shí),會(huì)被從WeakHashMap自動(dòng)刪除。

更精確的說(shuō),對(duì)于一個(gè)給定的鍵,其映射的存在并不能阻止垃圾回收器對(duì)該鍵的丟棄,這就使該鍵稱為被終止的,被終止,然后被回收,這樣,這就可以認(rèn)為該鍵值對(duì)應(yīng)該被WeakHashMap刪除。因此,WeakHashMap使用了弱引用作為內(nèi)部數(shù)據(jù)的存儲(chǔ)方案

WeakHashMap可以作為簡(jiǎn)單緩存表的解決方案,當(dāng)系統(tǒng)內(nèi)存不足時(shí),垃圾收集器會(huì)自動(dòng)的清除沒(méi)有在任何其他地方被引用的鍵值對(duì)。如果需要用一張很大的Map作為緩存表時(shí),那么可以考慮使用WeakHashMap。

從源碼的角度,我們來(lái)分析下上面這段話是如何來(lái)工作的??

在WeakHashMap實(shí)現(xiàn)中,借用了ReferenceQueue這個(gè)“監(jiān)聽(tīng)器”來(lái)保存被GC回收的”弱鍵”,然后在每次使用WeakHashMap時(shí),就在WeakHashMap中刪除ReferenceQueue中保存的鍵值對(duì)。即WeakHashMap的實(shí)現(xiàn)是通過(guò)借用 ReferenceQueue這個(gè)“監(jiān)聽(tīng)器”來(lái)優(yōu)雅的實(shí)現(xiàn)自動(dòng)刪除那些引用不可達(dá)的key的。關(guān)于ReferenceQueue會(huì)在下篇博文中進(jìn)行介紹

具體如下:

WeakHashMap是通過(guò)數(shù)組table保存Entry(鍵值對(duì));每個(gè)Entry實(shí)際上就是一個(gè)鏈表來(lái)實(shí)現(xiàn)的。當(dāng)某“弱鍵”不再被其它對(duì)象引用,就會(huì)被GC回收時(shí),這個(gè)“弱鍵”也同時(shí)被添加到ReferenceQueue隊(duì)列中。當(dāng)下一步我們需要操作WeakHashMap時(shí),會(huì)先同步table、queue,table中保存了全部的鍵值對(duì),而queue中保存的是GC回收的鍵值對(duì);同步他們,就是刪除table中被GC回收的鍵值對(duì)。

源碼中完成“刪除”操作的函數(shù)代碼如下:

    /**
     * Expunges stale entries from the table.
     *翻譯:刪除過(guò)時(shí)的條目,即將ReferenceQueue隊(duì)列中的對(duì)象引用全部在table中給刪除掉
     *思路:如何刪除一個(gè)table的節(jié)點(diǎn)e,方法為:首先計(jì)算e的hash值,接著根據(jù)hash值找到其在table的位置,然后遍歷鏈表即可。
     */
    private void expungeStaleEntries() {
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>) x;
                int i = indexFor(e.hash, table.length);
                Entry<K,V> prev = table[i];
                Entry<K,V> p = prev;
                while (p != null) {
                    Entry<K,V> next = p.next;
                    if (p == e) {
                        if (prev == e)
                            table[i] = next;
                        else
                            prev.next = next;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }

例子說(shuō)明1

往一個(gè)WeakHashMap中添加大量的元素

上面說(shuō)的可能比較空,比如為什么可以作為緩沖表呀之類,可能看一個(gè)實(shí)際例子之后我們就可以更好的理解上面的兩段話

第一段代碼,就是HashMap的應(yīng)用,往HashMap中存放一系列很大的數(shù)據(jù)。

    public class TestHashMap {
        public static void main(String[] args){
            Map<Integer,byte[]> hashMap = new HashMap<Integer,byte[]>();
            for(int i=0;i<100000;i++){
                hashMap.put(i, new byte[i]);
            }
        }
    }

第二段代碼,就是WeakHashMap的應(yīng)用,往WeakHashMap中存放與上例HashMap相同的數(shù)據(jù)。

    public class TestWeakHashMap {
        public static void main(String[] args){
            Map<Integer,byte[]> weakHashMap = new WeakHashMap<Integer,byte[]>();
            for(int i=0;i<100000;i++){
                weakHashMap.put(i, new byte[i]);
            }
        }
    }

運(yùn)行上面的兩段代碼,發(fā)現(xiàn),第一段代碼是不能正常工作的,會(huì)拋“java.lang.OutOfMemoryError: Java heap space”,而第二段代碼就可以正常工作。

以上就說(shuō)明了,WeakHashMap當(dāng)系統(tǒng)內(nèi)存不足時(shí),垃圾收集器會(huì)自動(dòng)的清除沒(méi)有在任何其他地方被引用的鍵值對(duì),因此可以作為簡(jiǎn)單緩存表的解決方案。而HashMap就沒(méi)有上述功能。

但是,如果WeakHashMap的key在系統(tǒng)內(nèi)持有強(qiáng)引用,那么WeakHashMap就退化為了HashMap,所有的表項(xiàng)都不會(huì)被垃圾回收器回收。

例子說(shuō)明2

一系列的WeakHashMap,往每個(gè)WeakHashMap中只添加一個(gè)大的數(shù)據(jù)

看如下的例子,例子的代碼是,在for循環(huán)中每次都new一個(gè)WeakHashMap對(duì)象,且每個(gè)對(duì)象實(shí)例中只添加一個(gè)key和value都是大的數(shù)組對(duì)象。看會(huì)出現(xiàn)上面現(xiàn)象???

    public class TestWeakHashMap3 {
        public static void main(String[] args){
            List<WeakHashMap<Integer[][], Integer[][]>> maps = new ArrayList<WeakHashMap<Integer[][],Integer[][]>>();   
            int totalNum = 10000;
            for(int i=0;i<totalNum;i++){
                WeakHashMap<Integer[][], Integer[][]> w = new WeakHashMap<Integer[][], Integer[][]>();
                w.put(new Integer[1000][1000], new Integer[1000][1000]);
                maps.add(w);
                System.gc();//顯示gc
                System.out.println(i);
            }
        }
    }

上面的運(yùn)行結(jié)果如下:即由于空間不足報(bào)異常錯(cuò)誤。

    /*
     * 運(yùn)行結(jié)果:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
     *  at com.wrh.testhashmap.TestWeakHashMap3.main(TestWeakHashMap3.java:15)
     * */

而如下的代碼確能夠正常工作,這兩段代碼的區(qū)別在于下面這段代碼中調(diào)用了WeakHashMap的size()方法。

    public class TestWeakHashMap5 {
        public static void main(String[] args){
            List<WeakHashMap<Integer[][], Integer[][]>> maps = new ArrayList<WeakHashMap<Integer[][],Integer[][]>>();   
            int totalNum = 10000;
            for(int i=0;i<totalNum;i++){
                WeakHashMap<Integer[][], Integer[][]> w = new WeakHashMap<Integer[][], Integer[][]>();
                w.put(new Integer[1000][1000], new Integer[1000][1000]);
                maps.add(w);
                System.gc();
                for(int j=0;j<i;j++){
                    System.out.println("第"+j+"個(gè)map的大小為:"+maps.get(j).size());
                }
            }
        }
    }

可能有人要問(wèn)了,不是說(shuō)WeakHashMap具有會(huì)自動(dòng)進(jìn)行垃圾回收,第一種情況為什么會(huì)報(bào)OOM異常了,第二種情況會(huì)正常工作呢????

首先要說(shuō)明的是,第一段代碼并不是沒(méi)有執(zhí)行GC,而是僅對(duì)WeakHashMap中的key中的Integer數(shù)組進(jìn)行了回收,而value依然保持。我們先來(lái)看如下的例子:將value換成一個(gè)小的對(duì)象Object,就會(huì)證明這一點(diǎn)內(nèi)容。

    public static void main(String[] args){
        List<WeakHashMap<Integer[][], Object>> maps = new ArrayList<WeakHashMap<Integer[][],Object>>(); 
        int totalNum = 10000;
        for(int i=0;i<totalNum;i++){
            WeakHashMap<Integer[][], Object> w = new WeakHashMap<Integer[][], Object>();
            w.put(new Integer[1000][1000], new Object());
            maps.add(w);
            System.gc();
            System.out.println(i);
        }
    }

上面的代碼運(yùn)行時(shí)沒(méi)有任何問(wèn)題的,這也就證明了key中的Integer數(shù)組確實(shí)被回收了,那為何key中的reference數(shù)據(jù)被GC,卻沒(méi)有觸發(fā)WeakHashMap去做清理整個(gè)key的操作呢??

原因是在于:在進(jìn)行put操作后,雖然GC將WeakReference的key中的Integer數(shù)組回收了,并將事件通過(guò)到了ReferenceQueue,但是后續(xù)卻沒(méi)有相應(yīng)的動(dòng)作去觸發(fā)WeakHashMap來(lái)進(jìn)行處理ReferenceQueue,所以WeakReference包裝的key依然存在在WeakHashMap中,其對(duì)應(yīng)的value也就依然存在。

但是在WeakHashMap中會(huì)刪除那些已經(jīng)被GC的鍵值對(duì)在源碼中是通過(guò)調(diào)用expungeStaleEntries函數(shù)來(lái)完成的,而這個(gè)函數(shù)只在WeakHashMap的put、get、size()等方法中才進(jìn)行了調(diào)用。因此,只有put、get、size()方法來(lái)可以觸發(fā)WeakHashMap來(lái)進(jìn)行處理ReferenceQueue。

以上也就是為什么上面的第二段代碼中調(diào)用下WeakHashMap的size()方法之后就不會(huì)報(bào)異常能正常工作的原因。

到此這篇關(guān)于Java中WeakHashMap和HashMap的區(qū)別詳解的文章就介紹到這了,更多相關(guān)WeakHashMap和HashMap的區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 以Java代碼為例講解設(shè)計(jì)模式中的簡(jiǎn)單工廠模式

    以Java代碼為例講解設(shè)計(jì)模式中的簡(jiǎn)單工廠模式

    簡(jiǎn)單來(lái)說(shuō),工廠模式就是按照需求來(lái)返回一個(gè)類型的對(duì)象,使用工廠模式的意義就是,如果對(duì)象的實(shí)例化與代碼依賴太大的話,不方便進(jìn)行擴(kuò)展和維護(hù),使用工廠的目的就是使對(duì)象的實(shí)例化與主程序代碼就行解耦.來(lái)具體看一下:
    2016-05-05
  • Java語(yǔ)言描述二叉樹(shù)的深度和寬度

    Java語(yǔ)言描述二叉樹(shù)的深度和寬度

    這篇文章主要介紹了Java語(yǔ)言描述二叉樹(shù)的深度和寬度,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • java 線程池的實(shí)現(xiàn)原理、優(yōu)點(diǎn)與風(fēng)險(xiǎn)、以及4種線程池實(shí)現(xiàn)

    java 線程池的實(shí)現(xiàn)原理、優(yōu)點(diǎn)與風(fēng)險(xiǎn)、以及4種線程池實(shí)現(xiàn)

    這篇文章主要介紹了java 線程池的實(shí)現(xiàn)原理、優(yōu)點(diǎn)與風(fēng)險(xiǎn)、以及4種線程池實(shí)現(xiàn)包括了:配置線程池大小配置,線程池的實(shí)現(xiàn)原理等,需要的朋友可以參考下
    2023-02-02
  • SpringBoot讀取Resource目錄下文件的四種方式總結(jié)

    SpringBoot讀取Resource目錄下文件的四種方式總結(jié)

    在Spring?Boot項(xiàng)目中,經(jīng)常需要獲取resources目錄下的文件,這些文件可以包括配置文件、模板文件、靜態(tài)資源等,本文將介紹四種常用的方法來(lái)獲取resources目錄下的文件,需要的朋友可以參考下
    2023-08-08
  • 10個(gè)Java程序員熟悉的面向?qū)ο笤O(shè)計(jì)原則

    10個(gè)Java程序員熟悉的面向?qū)ο笤O(shè)計(jì)原則

    這篇文章主要為大家詳細(xì)介紹了Java程序員應(yīng)當(dāng)知道的10個(gè)面向?qū)ο笤O(shè)計(jì)原則,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • 詳解JAVA8 函數(shù)式接口

    詳解JAVA8 函數(shù)式接口

    這篇文章主要介紹了JAVA8 函數(shù)式接口的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • SpringBoot整合MybatisPlusGernerator實(shí)現(xiàn)逆向工程

    SpringBoot整合MybatisPlusGernerator實(shí)現(xiàn)逆向工程

    在我們寫項(xiàng)目的時(shí)候,我們時(shí)常會(huì)因?yàn)樾枰獎(jiǎng)?chuàng)建很多的項(xiàng)目結(jié)構(gòu)而頭疼,本文主要介紹了SpringBoot整合MybatisPlusGernerator實(shí)現(xiàn)逆向工程,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • java格式化數(shù)值成貨幣格式示例

    java格式化數(shù)值成貨幣格式示例

    這篇文章主要介紹了java格式化數(shù)值成貨幣格式示例,格式化一個(gè)數(shù)值,比如123456789.123,希望顯示成"$123,456,789.123",需要的朋友可以參考下
    2014-04-04
  • IDEA整合jeesite4.x及安裝教程

    IDEA整合jeesite4.x及安裝教程

    本文給大家介紹IDEA整合jeesite4.x及安裝教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • SpringBoot動(dòng)態(tài)表操作服務(wù)的實(shí)現(xiàn)代碼

    SpringBoot動(dòng)態(tài)表操作服務(wù)的實(shí)現(xiàn)代碼

    在現(xiàn)代的應(yīng)用開(kāi)發(fā)中,尤其是在數(shù)據(jù)庫(kù)設(shè)計(jì)不斷變化的情況下,動(dòng)態(tài)操作數(shù)據(jù)庫(kù)表格成為了不可或缺的一部分,在本篇文章中,我們將以一個(gè)典型的動(dòng)態(tài)表操作服務(wù)為例,詳細(xì)介紹如何在 Spring Boot 中使用 JdbcTemplate 實(shí)現(xiàn)動(dòng)態(tài)表管理,需要的朋友可以參考下
    2025-01-01

最新評(píng)論