java中利用WeakHashMap實現緩存


簡介

WeakHashMap是Java集合框架里的一員,從名字可以看出它是某種 Map。它的特殊之處在於 WeakHashMap 里的entry可能會被GC自動刪除,即使程序員沒有調用remove()或者clear()方法。

更直觀的說,當使用 WeakHashMap 時,即使沒有顯示的添加或刪除任何元素,也可能發生如下情況:

  • 調用兩次size()方法返回不同的值;
  • 兩次調用isEmpty()方法,第一次返回false,第二次返回true
  • 兩次調用containsKey()方法,第一次返回true,第二次返回false,盡管兩次使用的是同一個key
  • 兩次調用get()方法,第一次返回一個value,第二次返回null,盡管兩次使用的是同一個對象。

遇到這么奇葩的現象,你是不是覺得使用者一定會瘋掉?其實不然,WeekHashMap 的這個特點特別適用於需要緩存的場景。在緩存場景下,由於內存是有限的,不能緩存所有對象;對象緩存命中可以提高系統效率,但緩存MISS也不會造成錯誤,因為可以通過計算重新得到。

要明白 WeekHashMap 的工作原理,還需要引入一個概念:弱引用(WeakReference)。我們都知道Java中內存是通過GC自動管理的,GC會在程序運行過程中自動判斷哪些對象是可以被回收的,並在合適的時機進行內存釋放。GC判斷某個對象是否可被回收的依據是,是否有有效的引用指向該對象。如果沒有有效引用指向該對象(基本意味着不存在訪問該對象的方式),那么該對象就是可回收的。這里的“有效引用”並不包括弱引用。也就是說,雖然弱引用可以用來訪問對象,但進行垃圾回收時弱引用並不會被考慮在內,僅有弱引用指向的對象仍然會被GC回收

WeakHashMap 內部是通過弱引用來管理entry的,弱引用的特性對應到 WeakHashMap 上意味着什么呢?將一對key, value放入到 WeakHashMap 里並不能避免該key值被GC回收,除非在 WeakHashMap 之外還有對該key的強引用

關於強引用,弱引用等概念以后再具體講解,這里只需要知道Java中引用也是分種類的,並且不同種類的引用對GC的影響不同就夠了。

是否有WeekHashSet?

答案是沒有:( 。不過Java Collections工具類給出了解決方案,Collections.newSetFromMap(Map<E,Boolean> map)方法可以將任何 Map包裝成一個Set。通過如下方式可以快速得到一個 Weak HashSet:

// 將WeakHashMap包裝成一個Set
Set<Object> weakHashSet = Collections.newSetFromMap(
        new WeakHashMap<Object, Boolean>());

不出你所料,newSetFromMap()方法只是對傳入的 Map做了簡單包裝:

// Collections.newSetFromMap()用於將任何Map包裝成一個Set
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
    return new SetFromMap<>(map);
}
 
private static class SetFromMap<E> extends AbstractSet<E>
    implements Set<E>, Serializable
{
    private final Map<E, Boolean> m;  // The backing map
    private transient Set<E> s;       // Its keySet
    SetFromMap(Map<E, Boolean> map) {
        if (!map.isEmpty())
            throw new IllegalArgumentException("Map is non-empty");
        m = map;
        s = map.keySet();
    }
    public void clear()               {        m.clear(); }
    public int size()                 { return m.size(); }
    public boolean isEmpty()          { return m.isEmpty(); }
    public boolean contains(Object o) { return m.containsKey(o); }
    public boolean remove(Object o)   { return m.remove(o) != null; }
    public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
    public Iterator<E> iterator()     { return s.iterator(); }
    public Object[] toArray()         { return s.toArray(); }
    public <T> T[] toArray(T[] a)     { return s.toArray(a); }
    public String toString()          { return s.toString(); }
    public int hashCode()             { return s.hashCode(); }
    public boolean equals(Object o)   { return o == this || s.equals(o); }
    public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
    public boolean removeAll(Collection<?> c)   {return s.removeAll(c);}
    public boolean retainAll(Collection<?> c)   {return s.retainAll(c);}
    // addAll is the only inherited implementation
    ......
}

 

// Collections.newSetFromMap()用於將任何Map包裝成一個Set
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
     return new SetFromMap<>(map);
}
 
private static class SetFromMap<E> extends AbstractSet<E>
     implements Set<E>, Serializable
{
     private final Map<E, Boolean> m;  // The backing map
     private transient Set<E> s;       // Its keySet
     SetFromMap(Map<E, Boolean> map) {
         if (!map.isEmpty())
             throw new IllegalArgumentException( "Map is non-empty" );
         m = map;
         s = map.keySet();
     }
     public void clear()               {        m.clear(); }
     public int size()                 { return m.size(); }
     public boolean isEmpty()          { return m.isEmpty(); }
     public boolean contains(Object o) { return m.containsKey(o); }
     public boolean remove(Object o)   { return m.remove(o) != null ; }
     public boolean add(E e) { return m.put(e, Boolean.TRUE) == null ; }
     public Iterator<E> iterator()     { return s.iterator(); }
     public Object[] toArray()         { return s.toArray(); }
     public <T> T[] toArray(T[] a)     { return s.toArray(a); }
     public String toString()          { return s.toString(); }
     public int hashCode()             { return s.hashCode(); }
     public boolean equals(Object o)   { return o == this || s.equals(o); }
     public boolean containsAll(Collection<?> c) { return s.containsAll(c);}
     public boolean removeAll(Collection<?> c)   { return s.removeAll(c);}
     public boolean retainAll(Collection<?> c)   { return s.retainAll(c);}
     // addAll is the only inherited implementation
     ......
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM