Java WeakHashMap 到底Weak在哪里,它真的很弱嗎?WeakHashMap 的適用場(chǎng)景是什么,使用時(shí)需要注意些什么?弱引用和強(qiáng)引用對(duì)Java GC有什么不同影響?本文將給出清晰而簡(jiǎn)潔的介紹。
總體介紹
在Java集合框架系列文章的最后,筆者打算介紹一個(gè)特殊的成員:WeakHashMap,從名字可以看出它是某種 Map。它的特殊之處在于 WeakHashMap 里的entry
可能會(huì)被GC自動(dòng)刪除,即使程序員沒(méi)有調(diào)用remove()
或者clear()
方法。
更直觀的說(shuō),當(dāng)使用 WeakHashMap 時(shí),即使沒(méi)有顯示的添加或刪除任何元素,也可能發(fā)生如下情況:
- 調(diào)用兩次
size()
方法返回不同的值; - 兩次調(diào)用
isEmpty()
方法,第一次返回false
,第二次返回true
; - 兩次調(diào)用
containsKey()
方法,第一次返回true
,第二次返回false
,盡管兩次使用的是同一個(gè)key
; - 兩次調(diào)用
get()
方法,第一次返回一個(gè)value
,第二次返回null
,盡管兩次使用的是同一個(gè)對(duì)象。
遇到這么奇葩的現(xiàn)象,你是不是覺(jué)得使用者一定會(huì)瘋掉?其實(shí)不然,WeekHashMap 的這個(gè)特點(diǎn)特別適用于需要緩存的場(chǎng)景。在緩存場(chǎng)景下,由于內(nèi)存是有限的,不能緩存所有對(duì)象;對(duì)象緩存命中可以提高系統(tǒng)效率,但緩存MISS也不會(huì)造成錯(cuò)誤,因?yàn)榭梢酝ㄟ^(guò)計(jì)算重新得到。
要明白 WeekHashMap 的工作原理,還需要引入一個(gè)概念:弱引用(WeakReference)。我們都知道Java中內(nèi)存是通過(guò)GC自動(dòng)管理的,GC會(huì)在程序運(yùn)行過(guò)程中自動(dòng)判斷哪些對(duì)象是可以被回收的,并在合適的時(shí)機(jī)進(jìn)行內(nèi)存釋放。GC判斷某個(gè)對(duì)象是否可被回收的依據(jù)是,是否有有效的引用指向該對(duì)象。如果沒(méi)有有效引用指向該對(duì)象(基本意味著不存在訪問(wèn)該對(duì)象的方式),那么該對(duì)象就是可回收的。這里的“有效引用”并不包括弱引用。也就是說(shuō),雖然弱引用可以用來(lái)訪問(wèn)對(duì)象,但進(jìn)行垃圾回收時(shí)弱引用并不會(huì)被考慮在內(nèi),僅有弱引用指向的對(duì)象仍然會(huì)被GC回收。
WeakHashMap 內(nèi)部是通過(guò)弱引用來(lái)管理entry
的,弱引用的特性對(duì)應(yīng)到 WeakHashMap 上意味著什么呢?將一對(duì)key, value
放入到 WeakHashMap 里并不能避免該key
值被GC回收,除非在 WeakHashMap 之外還有對(duì)該key
的強(qiáng)引用。
關(guān)于強(qiáng)引用,弱引用等概念以后再具體講解,這里只需要知道Java中引用也是分種類(lèi)的,并且不同種類(lèi)的引用對(duì)GC的影響不同就夠了。
具體實(shí)現(xiàn)
WeakHashMap的存儲(chǔ)結(jié)構(gòu)類(lèi)似于HashMap,讀者可自行參考前文,這里不再贅述。
關(guān)于強(qiáng)弱引用的管理方式,博主將會(huì)另開(kāi)專(zhuān)題單獨(dú)講解。
Weak HashSet?
如果你看過(guò)前幾篇關(guān)于 Map 和 Set 的講解,一定會(huì)問(wèn):既然有 WeekHashMap,是否有 WeekHashSet 呢?答案是沒(méi)有:( 。不過(guò)Java Collections工具類(lèi)給出了解決方案,Collections.newSetFromMap(Map<E,Boolean> map)
方法可以將任何 Map包裝成一個(gè)Set。通過(guò)如下方式可以快速得到一個(gè) Weak HashSet:
// 將WeakHashMap包裝成一個(gè)Set
Set<Object> weakHashSet = Collections.newSetFromMap(
new WeakHashMap<Object, Boolean>());
不出你所料,newSetFromMap()
方法只是對(duì)傳入的 Map做了簡(jiǎn)單包裝:
// Collections.newSetFromMap()用于將任何Map包裝成一個(gè)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


}
結(jié)語(yǔ)
至此深入Java集合框架(Java Collections Framework Internals)系列已經(jīng)全部講解完畢,希望這幾篇簡(jiǎn)短的博文能夠幫助各位讀者對(duì)Java容器框架建立基本的理解。通過(guò)這里可以返回本系列文章目錄。
如果對(duì)各位有哪怕些微的幫助,博主將感到非常高興!如果博文中有任何的紕漏和謬誤,歡迎各位博友指正。
本文GitHub地址
posted on 2016-05-31 07:27
CarpenterLee 閱讀(1119)
評(píng)論(0) 編輯 收藏