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

Java中WeakHashMap的使用詳解

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

WeakHashMap

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

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

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

先來想象一下你因為某種需求需要一個Cache,你肯定會面臨一個問題,就是所有數(shù)據(jù)不可能都放到Cache里,或者放到Cache里性價比太低了。

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

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

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

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

某個WeakReference對象所指向的對象如果被判定為垃圾對象,Jvm會將該WeakReference對象放到一個ReferenceQueue里,我們只要看下這個Queue里的內(nèi)容就知道某個對象還有沒有用了。 WeakHashMap就是這么做的,所以這里的Weak是指WeakReference。接下來讓我們看下它的代碼,看它具體是怎么實現(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也有一些默認值,比如默認容量是16,最大容量2^30,使用超過75%它就會自動擴容。

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),然后傳入了一個ReferenceQueue,這就讓它能在某個key失去所有強引用的時候感知到。

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

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

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

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

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方法就沒什么特別的了,因為Entry里存了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的核心了,它承擔著Map中死對象的清理工作。原理就是依賴WeakReference和ReferenceQueue的特性。

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

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

其他

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

缺陷

1.非線程安全

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

2.單純作為Map沒有HashMap好

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

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

3.不能自定義ReferenceQueue

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

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

用途

這里列舉幾個我所知道的WeakHashMap的使用場景。

1.阿里Arthas

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

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

2.Cache

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

3.ThreadLocalMap

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

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

相關(guān)文章

  • springboot websocket簡單入門示例

    springboot websocket簡單入門示例

    這篇文章主要介紹了springboot websocket簡單入門示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • 從?PageHelper?到?MyBatis?Plugin執(zhí)行概要及實現(xiàn)原理

    從?PageHelper?到?MyBatis?Plugin執(zhí)行概要及實現(xiàn)原理

    這篇文章主要為大家介紹了從?PageHelper?到?MyBatis?Plugin執(zhí)行概要及實現(xiàn)原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Java數(shù)據(jù)結(jié)構(gòu)之隊列與OJ題

    Java數(shù)據(jù)結(jié)構(gòu)之隊列與OJ題

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之隊列與OJ題,本文章先是對隊列進行介紹,后又介紹了四道OJ相關(guān)的題目,來使其深入理解,需要的朋友可以參考下
    2023-01-01
  • 并發(fā)編程之Java內(nèi)存模型

    并發(fā)編程之Java內(nèi)存模型

    這篇文章主要介紹了Java并發(fā)編程之內(nèi)存模型,Java內(nèi)存模型中的順序一致性,主要介紹重排序與順序一致性內(nèi)存模型,下面文章將圍繞Java內(nèi)存模型展開內(nèi)容,需要的小伙伴可以參考一下
    2021-11-11
  • JavaWeb三大組件之Filter過濾器詳解

    JavaWeb三大組件之Filter過濾器詳解

    這篇文章主要介紹了JavaWeb三大組件之Filter過濾器詳解,過濾器Filter是Java?Web應(yīng)用中的一種組件,它在請求到達Servlet或JSP之前或者響應(yīng)送回客戶端之前,對請求和響應(yīng)進行預(yù)處理和后處理操作,需要的朋友可以參考下
    2023-10-10
  • Redis實現(xiàn)延遲隊列的全流程詳解

    Redis實現(xiàn)延遲隊列的全流程詳解

    Redisson是Redis服務(wù)器上的分布式可伸縮Java數(shù)據(jù)結(jié)構(gòu),這篇文中主要為大家介紹了Redisson實現(xiàn)的優(yōu)雅的延遲隊列的方法,需要的可以參考一下
    2023-03-03
  • 反編譯jar實現(xiàn)的三種方式

    反編譯jar實現(xiàn)的三種方式

    本文主要介紹了反編譯jar實現(xiàn)的三種方式,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java logback日志的簡單使用

    Java logback日志的簡單使用

    這篇文章主要介紹了Java logback日志的使用詳解,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下
    2021-03-03
  • 使用Java編寫導出不確定行數(shù)列數(shù)數(shù)據(jù)的工具類

    使用Java編寫導出不確定行數(shù)列數(shù)數(shù)據(jù)的工具類

    這篇文章主要為大家詳細介紹了如何使用Java編寫導出不確定行數(shù)列數(shù)數(shù)據(jù)的工具類,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-03-03
  • Java集合框架中迭代器Iterator解析

    Java集合框架中迭代器Iterator解析

    這篇文章主要為大家簡單介紹了Java集合框架中迭代器Iterator的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03

最新評論