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

Java中WeakHashMap的使用詳解

 更新時(shí)間:2023年09月06日 11:24:29   作者:xindoo  
這篇文章主要介紹了Java中WeakHashMap的使用詳解,WeakHashMap是一種弱引用的Map,底層數(shù)據(jù)結(jié)構(gòu)為數(shù)組鏈表,與HashMap相比,WeakHashMap的區(qū)別在于它的key存儲(chǔ)為弱引用,在垃圾回收時(shí),如果key沒有被強(qiáng)引用所引用,那么key會(huì)被回收掉,需要的朋友可以參考下

WeakHashMap

作為一個(gè)java開發(fā)者肯定都知道且使用HashMap,但估計(jì)大部分人都不太知道WeakHashMap。

從類定義上來看,它和普通的HashMap一樣,繼承了AbstractMap類和實(shí)現(xiàn)了Map接口,也就是說它有著與HashMap差不多的功能。

那么既然jdk已經(jīng)提供了HashMap,為什么還要再提供一個(gè)WeakHashMap呢? 黑格爾曾經(jīng)說過,存在必合理,接下來我們來看下為什么有WeakHashMap。   

先來想象一下你因?yàn)槟撤N需求需要一個(gè)Cache,你肯定會(huì)面臨一個(gè)問題,就是所有數(shù)據(jù)不可能都放到Cache里,或者放到Cache里性價(jià)比太低了。

這個(gè)時(shí)候你可能很快就想到了各種Cache數(shù)據(jù)過期策略,目前也有一些優(yōu)秀的包提供了功能豐富的Cache,比如Google的Guava Cache,它支持?jǐn)?shù)據(jù)定期過期、LRU、LFU等策略,但它任然有可能會(huì)導(dǎo)致有用的數(shù)據(jù)被淘汰,沒用的數(shù)據(jù)遲遲不淘汰(如果策略使用得當(dāng)?shù)那闆r下這都是小概率事件)。   

如果我現(xiàn)在說有種機(jī)制,可以讓你Cache里不用的key數(shù)據(jù)自動(dòng)清理掉,用的還留著,沒有誤殺也沒有漏殺你信不信!沒錯(cuò)WeakHashMap就是能實(shí)現(xiàn)這種功能的東西,這也是它和普通的HashMap不同的地方——它有自清理的機(jī)制。   

如果讓你實(shí)現(xiàn)一種自清理的HashMap,你怎么做? 我的做法肯定是想辦法先知道某個(gè)Key肯定沒有在用了,然后清理到HashMap中對(duì)應(yīng)的K-V。

在JVM里一個(gè)對(duì)象沒用了是指沒有任何其他有用對(duì)象直接或者間接執(zhí)行它,具體點(diǎn)就是在GC過程中它是GCRoots不可達(dá)的。 Jvm提供了一種機(jī)制能讓我們感知到一個(gè)對(duì)象是否已經(jīng)變成了垃圾對(duì)象,這就是WeakReference,不了解WeakReference的可以看下我上一篇介紹博客Java弱引用(WeakReferences)。   

某個(gè)WeakReference對(duì)象所指向的對(duì)象如果被判定為垃圾對(duì)象,Jvm會(huì)將該WeakReference對(duì)象放到一個(gè)ReferenceQueue里,我們只要看下這個(gè)Queue里的內(nèi)容就知道某個(gè)對(duì)象還有沒有用了。 WeakHashMap就是這么做的,所以這里的Weak是指WeakReference。接下來讓我們看下它的代碼,看它具體是怎么實(shí)現(xiàn)的。

源碼分析

    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    private static final int MAXIMUM_CAPACITY = 1 << 30;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

和普通HashMap一樣,WeakHashMap也有一些默認(rèn)值,比如默認(rèn)容量是16,最大容量2^30,使用超過75%它就會(huì)自動(dòng)擴(kuò)容。

Entry

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;
        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
        /*
        * 其他代碼  
        */
}

它的Entry和普通HashMap的Entry最大的不同是它繼承了WeakReference,然后把Key做成了弱引用(注意只有Key沒有Value),然后傳入了一個(gè)ReferenceQueue,這就讓它能在某個(gè)key失去所有強(qiáng)引用的時(shí)候感知到。

put

    public V put(K key, V value) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);
        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }
        modCount++;
        Entry<K,V> e = tab[i];
        tab[i] = new Entry<>(k, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

put方法也很簡(jiǎn)單,用key的hashcode在tab中定位,然后判斷是否是已經(jīng)存在的key,已經(jīng)存在就替換舊值,否則就新建Entry,遇到多個(gè)不同的key有同樣的hashCode就采用開鏈的方式解決hash沖突。

注意這里和HashMap不太一樣的地方,HashMap會(huì)在鏈表太長的時(shí)候?qū)︽湵碜鰳浠褑捂湵磙D(zhuǎn)換為紅黑樹,防止極端情況下hashcode沖突導(dǎo)致的性能問題,但在WeakHashMap中沒有樹化。   

同樣,在size大于閾值的時(shí)候,WeakHashMap也對(duì)做resize的操作,也就是把tab擴(kuò)大一倍。WeakHashMap中的resize比HashMap中的resize要簡(jiǎn)單好懂些,但沒HashMap中的resize優(yōu)雅。

WeakHashMap中resize有另外一個(gè)額外的操作,就是expungeStaleEntries(),就是對(duì)tab中的死對(duì)象做清理,稍后會(huì)詳細(xì)介紹。

get

    public V get(Object key) {
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int index = indexFor(h, tab.length);
        Entry<K,V> e = tab[index];
        while (e != null) {
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        return null;
    }

get方法就沒什么特別的了,因?yàn)镋ntry里存了hash值和key的值,所以只要用indexFor定位到tab中的位置,然后遍歷一下單鏈表就知道了。

expungeStaleEntries

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

expungeStaleEntries就是WeakHashMap的核心了,它承擔(dān)著Map中死對(duì)象的清理工作。原理就是依賴WeakReference和ReferenceQueue的特性。

在每個(gè)WeakHashMap都有個(gè)ReferenceQueue queue,在Entry初始化的時(shí)候也會(huì)將queue傳給WeakReference,這樣當(dāng)某個(gè)可以key失去所有強(qiáng)應(yīng)用之后,其key對(duì)應(yīng)的WeakReference對(duì)象會(huì)被放到queue里,有了queue就知道需要清理哪些Entry里。這里也是整個(gè)WeakHashMap里唯一加了同步的地方。   

除了上文說的到resize中調(diào)用了expungeStaleEntries(),size()中也調(diào)用了這個(gè)清理方法。另外 getTable()也調(diào)了,這就意味著幾乎所有其他方法都間接調(diào)用了清理。

其他

除了上述幾個(gè)和HashMap不太一樣的地方外,WeakHashMap也提供了其他HashMap所有的方法,比如像remove、clean、putAll、entrySet…… 功能上幾乎可以完全替代HashMap,但WeakHashMap也有一些自己的缺陷。

缺陷

1.非線程安全

關(guān)鍵修改方法沒有提供任何同步,多線程環(huán)境下肯定會(huì)導(dǎo)致數(shù)據(jù)不一致的情況,所以使用時(shí)需要多注意。

2.單純作為Map沒有HashMap好

HashMap在Jdk8做了好多優(yōu)化,比如單鏈表在過長時(shí)會(huì)轉(zhuǎn)化為紅黑樹,降低極端情況下的操作復(fù)雜度。

但WeakHashMap沒有相應(yīng)的優(yōu)化,有點(diǎn)像jdk8之前的HashMap版本。

3.不能自定義ReferenceQueue

WeakHashMap構(gòu)造方法中沒法指定自定的ReferenceQueue,如果用戶想用ReferenceQueue做一些額外的清理工作的話就行不通了。

如果即想用WeakHashMap的功能,也想用ReferenceQueue,貌似得自己實(shí)現(xiàn)一套新的WeakHashMap了。

用途

這里列舉幾個(gè)我所知道的WeakHashMap的使用場(chǎng)景。

1.阿里Arthas

在阿里開源的Java診斷工具中使用了WeakHashMap做類-字節(jié)碼的緩存。

 // 類-字節(jié)碼緩存
    private final static Map<Class<?>/*Class*/, byte[]/*bytes of Class*/> classBytesCache
            = new WeakHashMap<Class<?>, byte[]>();

2.Cache

WeakHashMap這種自清理的機(jī)制,非常適合做緩存了。

3.ThreadLocalMap

ThreadLocal中用ThreadLocalMap存儲(chǔ)Thread對(duì)象,雖然ThreadLocalMap和WeakHashMap不是一個(gè)東西,但ThreadLocalMap也利用到了WeakReference的特性,功能用途很類似,所以我很好奇為什么ThreadLocalMap不直接用WeakHashMap呢!

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

相關(guān)文章

  • JavaSE static final及abstract修飾符實(shí)例解析

    JavaSE static final及abstract修飾符實(shí)例解析

    這篇文章主要介紹了JavaSE static final及abstract修飾符實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • java反射機(jī)制Reflection詳解

    java反射機(jī)制Reflection詳解

    在本篇文章里小編給大家分享了關(guān)于java反射機(jī)制Reflection的相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。
    2019-04-04
  • java實(shí)現(xiàn)猜數(shù)字小游戲(Swing版)

    java實(shí)現(xiàn)猜數(shù)字小游戲(Swing版)

    這篇文章主要介紹了java實(shí)現(xiàn)猜數(shù)字小游戲,Swing編程版的猜數(shù)字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Mybatis實(shí)現(xiàn)動(dòng)態(tài)排序方式

    Mybatis實(shí)現(xiàn)動(dòng)態(tài)排序方式

    這篇文章主要介紹了Mybatis實(shí)現(xiàn)動(dòng)態(tài)排序方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Spring?Boot如何在加載bean時(shí)優(yōu)先選擇我

    Spring?Boot如何在加載bean時(shí)優(yōu)先選擇我

    這篇文章主要介紹了Spring?Boot如何在加載bean時(shí)優(yōu)先選擇我,在?Spring?Boot?應(yīng)用程序中,我們可以采取三種方式實(shí)現(xiàn)自己的?bean?優(yōu)先加載,本文通過實(shí)例代碼給大家詳細(xì)講解,需要的朋友可以參考下
    2023-03-03
  • Java中Stream流的常用方法代碼示例

    Java中Stream流的常用方法代碼示例

    這篇文章主要介紹了Java中Stream流的常用方法代碼示例,Stream類中每一個(gè)方法都對(duì)應(yīng)集合上的一種操作,將真正的函數(shù)式編程引入到Java中,能 讓代碼更加簡(jiǎn)潔,極大地簡(jiǎn)化了集合的處理操作,提高了開發(fā)的效率和生產(chǎn)力,需要的朋友可以參考下
    2023-10-10
  • Maven Profile多環(huán)境構(gòu)建實(shí)戰(zhàn)指南

    Maven Profile多環(huán)境構(gòu)建實(shí)戰(zhàn)指南

    在現(xiàn)代企業(yè)級(jí)Java項(xiàng)目開發(fā)中,面對(duì)開發(fā)(dev)、測(cè)試(test)、預(yù)發(fā)布(staging)、生產(chǎn)(prod)等多套環(huán)境的差異化配置管理,本文將從實(shí)戰(zhàn)角度深入剖析Maven Profile的多環(huán)境構(gòu)建體系,需要的朋友可以參考下
    2025-05-05
  • SpringBoot中定位切點(diǎn)的兩種常用方法

    SpringBoot中定位切點(diǎn)的兩種常用方法

    有時(shí)候,我們使用AOP來進(jìn)行放的增強(qiáng),編寫切面類的時(shí)候,需要定位在哪個(gè)方法上試用該切面進(jìn)行增強(qiáng),本片文章主要講解兩種在SpringBoot中定位切點(diǎn)的方法,感興趣的可以了解一下
    2021-06-06
  • Debian配置JDK1.7 與Linux Java Helloworld

    Debian配置JDK1.7 與Linux Java Helloworld

    這篇文章主要介紹了Debian配置JDK1.7 與Linux Java Helloworld 的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • java中url任意跳轉(zhuǎn)漏洞的解決

    java中url任意跳轉(zhuǎn)漏洞的解決

    URL任意跳轉(zhuǎn)漏洞是一種安全漏洞,它發(fā)生在應(yīng)用程序沒有正確地驗(yàn)證目標(biāo)URL,允許攻擊者將用戶重定向到惡意網(wǎng)站,本文就來介紹一下漏洞的解決修復(fù)方案,感興趣的可以了解一下
    2024-06-06

最新評(píng)論

宁远县| 五大连池市| 丽水市| 沙田区| 临泽县| 永春县| 察雅县| 夹江县| 西贡区| 留坝县| 长治县| 方正县| 汤原县| 杂多县| 顺义区| 航空| 介休市| 临沭县| 武山县| 喜德县| 疏勒县| 漳浦县| 榕江县| 保定市| 武陟县| 林口县| 屏山县| 樟树市| 陵水| 陕西省| 泽州县| 工布江达县| 樟树市| 重庆市| 苏州市| 调兵山市| 吉首市| 翁牛特旗| 江津市| 石首市| 岳池县|