解析WeakHashMap與HashMap的區(qū)別詳解
更新時(shí)間:2013年05月17日 09:15:49 作者:
本篇文章是對WeakHashMap與HashMap的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
WeakHashMap,此種Map的特點(diǎn)是,當(dāng)除了自身有對key的引用外,此key沒有其他引用那么此map會自動(dòng)丟棄此值,
見實(shí)例:此例子中聲明了兩個(gè)Map對象,一個(gè)是HashMap,一個(gè)是WeakHashMap,同時(shí)向兩個(gè)map中放入a、b兩個(gè)對象,當(dāng)HashMap remove掉a 并且將a、b都指向null時(shí),WeakHashMap中的a將自動(dòng)被回收掉。出現(xiàn)這個(gè)狀況的原因是,對于a對象而言,當(dāng)HashMap remove掉并且將a指向null后,除了WeakHashMap中還保存a外已經(jīng)沒有指向a的指針了,所以WeakHashMap會自動(dòng)舍棄掉a,而對于b對象雖然指向了null,但HashMap中還有指向b的指針,所以
WeakHashMap將會保留
package test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
public class Test {
public static void main(String[] args) throws Exception {
String a = new String("a");
String b = new String("b");
Map weakmap = new WeakHashMap();
Map map = new HashMap();
map.put(a, "aaa");
map.put(b, "bbb");
weakmap.put(a, "aaa");
weakmap.put(b, "bbb");
map.remove(a);
a=null;
b=null;
System.gc();
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry en = (Map.Entry)i.next();
System.out.println("map:"+en.getKey()+":"+en.getValue());
}
Iterator j = weakmap.entrySet().iterator();
while (j.hasNext()) {
Map.Entry en = (Map.Entry)j.next();
System.out.println("weakmap:"+en.getKey()+":"+en.getValue());
}
}
}
先把問題說清楚:
WeakHashMap是主要通過expungeStaleEntries這個(gè)函數(shù)的來實(shí)現(xiàn)移除其內(nèi)部不用的條目從而達(dá)到的自動(dòng)釋放內(nèi)存的目的的.基本上只要對WeakHashMap的內(nèi)容進(jìn)行訪問就會調(diào)用這個(gè)函數(shù),從而達(dá)到清除其內(nèi)部不在為外部引用的條目。但是如果預(yù)先生成了WeakHashMap,而在GC以前又不曾訪問該WeakHashMap,那不是就不能釋放內(nèi)存了嗎?
對應(yīng)的兩個(gè)測試案例:
WeakHashMapTest1:
public class WeakHashMapTest1 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);}}}
由于Java默認(rèn)內(nèi)存是64M,所以再不改變內(nèi)存參數(shù)的情況下,該測試跑不了幾步循環(huán)就內(nèi)存溢出了。果不其然,WeakHashMap這個(gè)時(shí)候并沒有自動(dòng)幫我們釋放不用的內(nèi)存。
WeakHashMapTest2:
public class WeakHashMapTest2 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);
for (int j = 0; j < i; j++) {
System.err.println(j + " size" + maps.get(j).size());
}
}
}
}
這次測試輸出正常,不在出現(xiàn)內(nèi)存溢出問題.
總結(jié)來說:WeakHashMap并不是你啥也干他就能自動(dòng)釋放內(nèi)部不用的對象的,而是在你訪問它的內(nèi)容的時(shí)候釋放內(nèi)部不用的對象
問題講清楚了,現(xiàn)在我們來梳理一下.了解清楚其中的奧秘.
WeakHashMap實(shí)現(xiàn)弱引用,是因?yàn)樗腅ntry<K,V>是繼承自WeakReference<K>的
在WeakHashMap$Entry<K,V>的類定義及構(gòu)造函數(shù)里面是這樣寫的:
private static class Entry<K,V>
extends WeakReference<K>
implements Map.Entry<K,V> Entry(K key, V value, ReferenceQueue<K> queue,int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
請注意它構(gòu)造父類的語句:“super(key, queue);”,傳入的是key,因此key才是進(jìn)行弱引用的,value是直接強(qiáng)引用關(guān)聯(lián)在this.value之中.在System.gc()時(shí),key中的byte數(shù)組進(jìn)行了回收,而value依然保持(value被強(qiáng)關(guān)聯(lián)到entry上,entry又關(guān)聯(lián)在map中,map關(guān)聯(lián)在arrayList中.).
如何證明key中的byte被回收了呢?可以通過內(nèi)存溢出時(shí)導(dǎo)出的內(nèi)存鏡像進(jìn)行分析,也可以通過如下的小測試得出結(jié)論:
把上面的value用小對象代替,
for (int i = 0; i < 10000; i++) {
WeakHashMap<byte[][], Object> d = new WeakHashMap<byte[][], Object>();
d.put(new byte[1000][1000], new Object());
maps.add(d); System.gc();
System.err.println(i);
}
上面的代碼,即使執(zhí)行10000次也沒有問題,證明key中的byte數(shù)組確實(shí)被回收了。
for循環(huán)中每次都new一個(gè)新的WeakHashMap,在put操作后,雖然GC將WeakReference的key中的byte數(shù)組回收了,并將事件通知到了ReferenceQueue,但后續(xù)卻沒有相應(yīng)的動(dòng)作去觸發(fā) WeakHashMap 去處理 ReferenceQueue
所以 WeakReference 包裝的key依然存在在WeakHashMap中,其對應(yīng)的value也當(dāng)然存在。
那value是何時(shí)被清除的呢?
對兩個(gè)例子進(jìn)行分析可知,例子二中的maps.get(j).size()觸發(fā)了value的回收,那又如何觸發(fā)的呢.查看WeakHashMap源碼可知,size方法調(diào)用了expungeStaleEntries方法,該方法對vm要回收的的entry(quene中)進(jìn)行遍歷,并將entry的value置空,回收了內(nèi)存.
所以效果是key在GC的時(shí)候被清除,value在key清除后訪問WeakHashMap被清除.
疑問:key的quene與map的quene是同一個(gè)quene,poll操作會減少一個(gè)reference,那問題是key如果先被清除,expungeStaleEntries遍歷quene時(shí)那個(gè)被回收的key對應(yīng)的entry還能取出來么???
關(guān)于執(zhí)行System.GC時(shí),key中的byte數(shù)據(jù)如何被回收了,請見WeakReference referenceQuene
WeakHashMap
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
以弱鍵實(shí)現(xiàn)的基于哈希表的 Map。在 WeakHashMap 中,當(dāng)某個(gè)鍵不再正常使用時(shí),將自動(dòng)移除其條目。
更精確地說,對于一個(gè)給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。
丟棄某個(gè)鍵時(shí),其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實(shí)現(xiàn)有所不同。
null 值和 null 鍵都被支持。該類具有與 HashMap 類相似的性能特征,并具有相同的效能參數(shù)初始容量 和加載因子。
像大多數(shù)集合類一樣,該類是不同步的??梢允褂?Collections.synchronizedMap 方法來構(gòu)造同步的 WeakHashMap。
該類主要與這樣的鍵對象一起使用,其 equals 方法使用 == 運(yùn)算符來測試對象標(biāo)識。
一旦這種鍵被丟棄,就永遠(yuǎn)無法再創(chuàng)建了,所以,過段時(shí)間后在 WeakHashMap 中查找此鍵是不可能的,不必對其項(xiàng)已移除而感到驚訝。
該類十分適合與 equals 方法不是基于對象標(biāo)識的鍵對象一起使用,比如,String 實(shí)例。
然而,對于這種可重新創(chuàng)建的鍵對象,鍵若丟棄,就自動(dòng)移除 WeakHashMap 條目,這種表現(xiàn)令人疑惑。
WeakHashMap 類的行為部分取決于垃圾回收器的動(dòng)作,所以,幾個(gè)常見的(雖然不是必需的)Map 常量不支持此類。
因?yàn)槔厥掌髟谌魏螘r(shí)候都可能丟棄鍵,WeakHashMap 就像是一個(gè)被悄悄移除條目的未知線程。
特別地,即使對 WeakHashMap 實(shí)例進(jìn)行同步,并且沒有調(diào)用任何賦值方法,在一段時(shí)間后 ,size 方法也可能返回較小的值,
對于 isEmpty 方法,可能返回 false,然后返回 true,對于給定的鍵,containsKey 方法可能返回 true 然后返回 false,對于給定的鍵,
get 方法可能返回一個(gè)值,但接著返回 null,對于以前出現(xiàn)在映射中的鍵,put 方法返回 null,而 remove 方法返回 false,
對于鍵集、值集、項(xiàng)集進(jìn)行的檢查,生成的元素?cái)?shù)量越來越少。
WeakHashMap 中的每個(gè)鍵對象間接地存儲為一個(gè)弱引用的指示對象。因此,不管是在映射內(nèi)還是在映射之外,
只有在垃圾回收器清除某個(gè)鍵的弱引用之后,該鍵才會自動(dòng)移除。
實(shí)現(xiàn)注意事項(xiàng):WeakHashMap 中的值對象由普通的強(qiáng)引用保持。因此應(yīng)該小心謹(jǐn)慎,確保值對象不會直接或間接地強(qiáng)引用其自身的鍵,
因?yàn)檫@會阻止鍵的丟棄。注意,值對象可以通過 WeakHashMap 本身間接引用其對應(yīng)的鍵;
這就是說,某個(gè)值對象可能強(qiáng)引用某個(gè)其他的鍵對象,而與該鍵對象相關(guān)聯(lián)的值對象轉(zhuǎn)而強(qiáng)引用第一個(gè)值對象的鍵。
處理此問題的一種方法是,在插入前將值自身包裝在 WeakReferences 中,如:m.put(key, new WeakReference(value)),
然后,分別用 get 進(jìn)行解包。
該類所有“collection 視圖方法”返回的迭代器均是快速失敗的:在迭代器創(chuàng)建之后,
如果從結(jié)構(gòu)上對映射進(jìn)行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時(shí)間任何方式的修改,
迭代器都將拋出 ConcurrentModificationException。因此,面對并發(fā)的修改,迭代器很快就完全失敗,
而不是冒著在將來不確定的時(shí)間任意發(fā)生不確定行為的風(fēng)險(xiǎn)。
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發(fā)修改時(shí),不可能作出任何堅(jiān)決的保證。
快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常程序的方式是錯(cuò)誤的,
正確做法是:迭代器的快速失敗行為應(yīng)該僅用于檢測 bug。
注意1:null 值和 null 鍵都被支持。
注意2:不是線程安全的。
注意3:迭代器的快速失敗行為不能得到保證。
注意4:WeakHashMap是無序的。
注意5:確保值對象不會直接或間接地強(qiáng)引用其自身的鍵,
因?yàn)檫@會阻止鍵的丟棄。但是,值對象可以通過 WeakHashMap 本身間接引用其對應(yīng)的鍵;
這就是說,某個(gè)值對象可能強(qiáng)引用某個(gè)其他的鍵對象,而與該鍵對象相關(guān)聯(lián)的值對象轉(zhuǎn)而強(qiáng)引用第一個(gè)值對象的鍵,這時(shí)就形成了環(huán)路。
處理此問題的一種方法是,在插入前將值自身包裝在WeakReferences中,如:m.put(key, new WeakReference(value)),
然后,分別用 get 進(jìn)行解包。如實(shí)例1.
實(shí)例1:
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
WeakHashMap<Integer,WeakReference<People>> map=new WeakHashMap<Integer,WeakReference<People>>();
People p1=new People("robin",1,28);
People p2=new People("robin",2,29);
People p3=new People("harry",3,30);
map.put(new Integer(100), new WeakReference<People>(p1));
map.put(new Integer(101), new WeakReference<People>(p2));
map.put(new Integer(1), new WeakReference<People>(p3));
for(WeakReference<People> rp:map.values())
{
People p= rp.get();
System.out.println("people:"+p);
}
}
}
class People{
String name;
int id;
int age;
public People(String name,int id)
{
this(name,id,0);
}
public People(String name,int id,int age)
{
this.name=name;
this.id=id;
this.age=age;
}
public String toString()
{
return id+name+age;
}
public boolean equals(Object o)
{
if(o==null)
return false;
if(!(o instanceof People))
return false;
People p=(People)o;
boolean res=name.equals(p.name);
if(res)
System.out.println("name "+name+" is double");
else
System.out.println(name+" vS "+p.name);
return res;
}
public int hashCode()
{
return name.hashCode();
}
}
見實(shí)例:此例子中聲明了兩個(gè)Map對象,一個(gè)是HashMap,一個(gè)是WeakHashMap,同時(shí)向兩個(gè)map中放入a、b兩個(gè)對象,當(dāng)HashMap remove掉a 并且將a、b都指向null時(shí),WeakHashMap中的a將自動(dòng)被回收掉。出現(xiàn)這個(gè)狀況的原因是,對于a對象而言,當(dāng)HashMap remove掉并且將a指向null后,除了WeakHashMap中還保存a外已經(jīng)沒有指向a的指針了,所以WeakHashMap會自動(dòng)舍棄掉a,而對于b對象雖然指向了null,但HashMap中還有指向b的指針,所以
WeakHashMap將會保留
復(fù)制代碼 代碼如下:
package test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
public class Test {
public static void main(String[] args) throws Exception {
String a = new String("a");
String b = new String("b");
Map weakmap = new WeakHashMap();
Map map = new HashMap();
map.put(a, "aaa");
map.put(b, "bbb");
weakmap.put(a, "aaa");
weakmap.put(b, "bbb");
map.remove(a);
a=null;
b=null;
System.gc();
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry en = (Map.Entry)i.next();
System.out.println("map:"+en.getKey()+":"+en.getValue());
}
Iterator j = weakmap.entrySet().iterator();
while (j.hasNext()) {
Map.Entry en = (Map.Entry)j.next();
System.out.println("weakmap:"+en.getKey()+":"+en.getValue());
}
}
}
先把問題說清楚:
WeakHashMap是主要通過expungeStaleEntries這個(gè)函數(shù)的來實(shí)現(xiàn)移除其內(nèi)部不用的條目從而達(dá)到的自動(dòng)釋放內(nèi)存的目的的.基本上只要對WeakHashMap的內(nèi)容進(jìn)行訪問就會調(diào)用這個(gè)函數(shù),從而達(dá)到清除其內(nèi)部不在為外部引用的條目。但是如果預(yù)先生成了WeakHashMap,而在GC以前又不曾訪問該WeakHashMap,那不是就不能釋放內(nèi)存了嗎?
對應(yīng)的兩個(gè)測試案例:
WeakHashMapTest1:
復(fù)制代碼 代碼如下:
public class WeakHashMapTest1 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);}}}
由于Java默認(rèn)內(nèi)存是64M,所以再不改變內(nèi)存參數(shù)的情況下,該測試跑不了幾步循環(huán)就內(nèi)存溢出了。果不其然,WeakHashMap這個(gè)時(shí)候并沒有自動(dòng)幫我們釋放不用的內(nèi)存。
WeakHashMapTest2:
復(fù)制代碼 代碼如下:
public class WeakHashMapTest2 {
public static void main(String[] args) throws Exception {
List<WeakHashMap<byte[][], byte[][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>();
for (int i = 0; i < 1000; i++) {
WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
d.put(new byte[1000][1000], new byte[1000][1000]);
maps.add(d);
System.gc();
System.err.println(i);
for (int j = 0; j < i; j++) {
System.err.println(j + " size" + maps.get(j).size());
}
}
}
}
這次測試輸出正常,不在出現(xiàn)內(nèi)存溢出問題.
總結(jié)來說:WeakHashMap并不是你啥也干他就能自動(dòng)釋放內(nèi)部不用的對象的,而是在你訪問它的內(nèi)容的時(shí)候釋放內(nèi)部不用的對象
問題講清楚了,現(xiàn)在我們來梳理一下.了解清楚其中的奧秘.
WeakHashMap實(shí)現(xiàn)弱引用,是因?yàn)樗腅ntry<K,V>是繼承自WeakReference<K>的
在WeakHashMap$Entry<K,V>的類定義及構(gòu)造函數(shù)里面是這樣寫的:
復(fù)制代碼 代碼如下:
private static class Entry<K,V>
extends WeakReference<K>
implements Map.Entry<K,V> Entry(K key, V value, ReferenceQueue<K> queue,int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
請注意它構(gòu)造父類的語句:“super(key, queue);”,傳入的是key,因此key才是進(jìn)行弱引用的,value是直接強(qiáng)引用關(guān)聯(lián)在this.value之中.在System.gc()時(shí),key中的byte數(shù)組進(jìn)行了回收,而value依然保持(value被強(qiáng)關(guān)聯(lián)到entry上,entry又關(guān)聯(lián)在map中,map關(guān)聯(lián)在arrayList中.).
如何證明key中的byte被回收了呢?可以通過內(nèi)存溢出時(shí)導(dǎo)出的內(nèi)存鏡像進(jìn)行分析,也可以通過如下的小測試得出結(jié)論:
把上面的value用小對象代替,
復(fù)制代碼 代碼如下:
for (int i = 0; i < 10000; i++) {
WeakHashMap<byte[][], Object> d = new WeakHashMap<byte[][], Object>();
d.put(new byte[1000][1000], new Object());
maps.add(d); System.gc();
System.err.println(i);
}
上面的代碼,即使執(zhí)行10000次也沒有問題,證明key中的byte數(shù)組確實(shí)被回收了。
for循環(huán)中每次都new一個(gè)新的WeakHashMap,在put操作后,雖然GC將WeakReference的key中的byte數(shù)組回收了,并將事件通知到了ReferenceQueue,但后續(xù)卻沒有相應(yīng)的動(dòng)作去觸發(fā) WeakHashMap 去處理 ReferenceQueue
所以 WeakReference 包裝的key依然存在在WeakHashMap中,其對應(yīng)的value也當(dāng)然存在。
那value是何時(shí)被清除的呢?
對兩個(gè)例子進(jìn)行分析可知,例子二中的maps.get(j).size()觸發(fā)了value的回收,那又如何觸發(fā)的呢.查看WeakHashMap源碼可知,size方法調(diào)用了expungeStaleEntries方法,該方法對vm要回收的的entry(quene中)進(jìn)行遍歷,并將entry的value置空,回收了內(nèi)存.
所以效果是key在GC的時(shí)候被清除,value在key清除后訪問WeakHashMap被清除.
疑問:key的quene與map的quene是同一個(gè)quene,poll操作會減少一個(gè)reference,那問題是key如果先被清除,expungeStaleEntries遍歷quene時(shí)那個(gè)被回收的key對應(yīng)的entry還能取出來么???
關(guān)于執(zhí)行System.GC時(shí),key中的byte數(shù)據(jù)如何被回收了,請見WeakReference referenceQuene
WeakHashMap
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
以弱鍵實(shí)現(xiàn)的基于哈希表的 Map。在 WeakHashMap 中,當(dāng)某個(gè)鍵不再正常使用時(shí),將自動(dòng)移除其條目。
更精確地說,對于一個(gè)給定的鍵,其映射的存在并不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然后被回收。
丟棄某個(gè)鍵時(shí),其條目從映射中有效地移除,因此,該類的行為與其他的 Map 實(shí)現(xiàn)有所不同。
null 值和 null 鍵都被支持。該類具有與 HashMap 類相似的性能特征,并具有相同的效能參數(shù)初始容量 和加載因子。
像大多數(shù)集合類一樣,該類是不同步的??梢允褂?Collections.synchronizedMap 方法來構(gòu)造同步的 WeakHashMap。
該類主要與這樣的鍵對象一起使用,其 equals 方法使用 == 運(yùn)算符來測試對象標(biāo)識。
一旦這種鍵被丟棄,就永遠(yuǎn)無法再創(chuàng)建了,所以,過段時(shí)間后在 WeakHashMap 中查找此鍵是不可能的,不必對其項(xiàng)已移除而感到驚訝。
該類十分適合與 equals 方法不是基于對象標(biāo)識的鍵對象一起使用,比如,String 實(shí)例。
然而,對于這種可重新創(chuàng)建的鍵對象,鍵若丟棄,就自動(dòng)移除 WeakHashMap 條目,這種表現(xiàn)令人疑惑。
WeakHashMap 類的行為部分取決于垃圾回收器的動(dòng)作,所以,幾個(gè)常見的(雖然不是必需的)Map 常量不支持此類。
因?yàn)槔厥掌髟谌魏螘r(shí)候都可能丟棄鍵,WeakHashMap 就像是一個(gè)被悄悄移除條目的未知線程。
特別地,即使對 WeakHashMap 實(shí)例進(jìn)行同步,并且沒有調(diào)用任何賦值方法,在一段時(shí)間后 ,size 方法也可能返回較小的值,
對于 isEmpty 方法,可能返回 false,然后返回 true,對于給定的鍵,containsKey 方法可能返回 true 然后返回 false,對于給定的鍵,
get 方法可能返回一個(gè)值,但接著返回 null,對于以前出現(xiàn)在映射中的鍵,put 方法返回 null,而 remove 方法返回 false,
對于鍵集、值集、項(xiàng)集進(jìn)行的檢查,生成的元素?cái)?shù)量越來越少。
WeakHashMap 中的每個(gè)鍵對象間接地存儲為一個(gè)弱引用的指示對象。因此,不管是在映射內(nèi)還是在映射之外,
只有在垃圾回收器清除某個(gè)鍵的弱引用之后,該鍵才會自動(dòng)移除。
實(shí)現(xiàn)注意事項(xiàng):WeakHashMap 中的值對象由普通的強(qiáng)引用保持。因此應(yīng)該小心謹(jǐn)慎,確保值對象不會直接或間接地強(qiáng)引用其自身的鍵,
因?yàn)檫@會阻止鍵的丟棄。注意,值對象可以通過 WeakHashMap 本身間接引用其對應(yīng)的鍵;
這就是說,某個(gè)值對象可能強(qiáng)引用某個(gè)其他的鍵對象,而與該鍵對象相關(guān)聯(lián)的值對象轉(zhuǎn)而強(qiáng)引用第一個(gè)值對象的鍵。
處理此問題的一種方法是,在插入前將值自身包裝在 WeakReferences 中,如:m.put(key, new WeakReference(value)),
然后,分別用 get 進(jìn)行解包。
該類所有“collection 視圖方法”返回的迭代器均是快速失敗的:在迭代器創(chuàng)建之后,
如果從結(jié)構(gòu)上對映射進(jìn)行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時(shí)間任何方式的修改,
迭代器都將拋出 ConcurrentModificationException。因此,面對并發(fā)的修改,迭代器很快就完全失敗,
而不是冒著在將來不確定的時(shí)間任意發(fā)生不確定行為的風(fēng)險(xiǎn)。
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發(fā)修改時(shí),不可能作出任何堅(jiān)決的保證。
快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常程序的方式是錯(cuò)誤的,
正確做法是:迭代器的快速失敗行為應(yīng)該僅用于檢測 bug。
注意1:null 值和 null 鍵都被支持。
注意2:不是線程安全的。
注意3:迭代器的快速失敗行為不能得到保證。
注意4:WeakHashMap是無序的。
注意5:確保值對象不會直接或間接地強(qiáng)引用其自身的鍵,
因?yàn)檫@會阻止鍵的丟棄。但是,值對象可以通過 WeakHashMap 本身間接引用其對應(yīng)的鍵;
這就是說,某個(gè)值對象可能強(qiáng)引用某個(gè)其他的鍵對象,而與該鍵對象相關(guān)聯(lián)的值對象轉(zhuǎn)而強(qiáng)引用第一個(gè)值對象的鍵,這時(shí)就形成了環(huán)路。
處理此問題的一種方法是,在插入前將值自身包裝在WeakReferences中,如:m.put(key, new WeakReference(value)),
然后,分別用 get 進(jìn)行解包。如實(shí)例1.
實(shí)例1:
復(fù)制代碼 代碼如下:
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
WeakHashMap<Integer,WeakReference<People>> map=new WeakHashMap<Integer,WeakReference<People>>();
People p1=new People("robin",1,28);
People p2=new People("robin",2,29);
People p3=new People("harry",3,30);
map.put(new Integer(100), new WeakReference<People>(p1));
map.put(new Integer(101), new WeakReference<People>(p2));
map.put(new Integer(1), new WeakReference<People>(p3));
for(WeakReference<People> rp:map.values())
{
People p= rp.get();
System.out.println("people:"+p);
}
}
}
class People{
String name;
int id;
int age;
public People(String name,int id)
{
this(name,id,0);
}
public People(String name,int id,int age)
{
this.name=name;
this.id=id;
this.age=age;
}
public String toString()
{
return id+name+age;
}
public boolean equals(Object o)
{
if(o==null)
return false;
if(!(o instanceof People))
return false;
People p=(People)o;
boolean res=name.equals(p.name);
if(res)
System.out.println("name "+name+" is double");
else
System.out.println(name+" vS "+p.name);
return res;
}
public int hashCode()
{
return name.hashCode();
}
}
相關(guān)文章
Java 前臺加后臺精品圖書管理系統(tǒng)的實(shí)現(xiàn)
相信每一個(gè)學(xué)生學(xué)編程的時(shí)候,應(yīng)該都會寫一個(gè)小項(xiàng)目——圖書管理系統(tǒng)。為什么這么說呢?我認(rèn)為一個(gè)學(xué)校的氛圍很大一部分可以從圖書館的氛圍看出來,而圖書管理系統(tǒng)這個(gè)不大不小的項(xiàng)目,接觸的多,也比較熟悉,不會有陌生感,能夠練手,又有些難度,所以我的小項(xiàng)目也來了2021-11-11Java8 使用 stream().sorted()對List集合進(jìn)行排序的操作
這篇文章主要介紹了Java8 使用 stream().sorted()對List集合進(jìn)行排序的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10小米Java程序員第二輪面試10個(gè)問題 你是否會被刷掉?
小米Java程序員第二輪面試10個(gè)問題,你是否會被刷掉?掌握好基礎(chǔ)知識,祝大家面試順利2017-11-11Java防止文件被篡改之文件校驗(yàn)功能的實(shí)例代碼
這篇文章主要介紹了Java防止文件被篡改之文件校驗(yàn)功能,本文給大家分享了文件校驗(yàn)和原理及具體實(shí)現(xiàn)思路,需要的朋友可以參考下2018-11-11springboot+jwt實(shí)現(xiàn)token登陸權(quán)限認(rèn)證的實(shí)現(xiàn)
這篇文章主要介紹了springboot+jwt實(shí)現(xiàn)token登陸權(quán)限認(rèn)證的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06