逍遥一派 发表于 2015-1-18 11:00:34

发布一篇Java汇合进修(十三) WeakHashMap具体先容(源码剖析)和利用示例 ...

微软什么都提供了。你可以试想一下,如果你是新手,你是希望你点一下按钮程序就能运行那,还是想自己一点一点的组织结构,然后打包发部,调错再打包......
这一章,我们对WeakHashMap举行进修。
我们先对WeakHashMap有个全体熟悉,然后再进修它的源码,最初再经由过程实例来学会利用WeakHashMap。
第1部分WeakHashMap先容
WeakHashMap简介
WeakHashMap承继于AbstractMap,完成了Map接口。
和HashMap一样,WeakHashMap也是一个散列表,它存储的内容也是键值对(key-value)映照,并且键和值都能够是null。
不外WeakHashMap的键是“弱键”。在WeakHashMap中,当某个键不再一般利用时,会被从WeakHashMap中被主动移除。更准确地说,关于一个给定的键,其映照的存在其实不制止渣滓接纳器对该键的抛弃,这就使该键成为可停止的,被停止,然后被接纳。某个键被停止时,它对应的键值对也就从映照中无效地移除。
这个“弱键”的道理呢?大抵上就是,经由过程WeakReference和ReferenceQueue完成的。WeakHashMap的key是“弱键”,便是WeakReference范例的;ReferenceQueue是一个行列,它会保留被GC接纳的“弱键”。完成步骤是:
(01)新建WeakHashMap,将“键值对”增加到WeakHashMap中。
实践上,WeakHashMap是经由过程数组table保留Entry(键值对);每个Entry实践上是一个单向链表,即Entry是键值对链表。
(02)当某“弱键”不再被别的对象援用,并被GC接纳时。在GC接纳该“弱键”时,这个“弱键”也同时会被增加到ReferenceQueue(queue)行列中。
(03)当下一次我们必要操纵WeakHashMap时,会先同步table和queue。table中保留了全体的键值对,而queue中保留被GC接纳的键值对;同步它们,就是删除table中被GC接纳的键值对。
这就是“弱键”怎样被主动从WeakHashMap中删除的步骤了。
和HashMap一样,WeakHashMap是分歧步的。可使用Collections.synchronizedMap办法来机关同步的WeakHashMap。
WeakHashMap的承继干系以下

java.lang.Object
java.util.AbstractMap<K,V>
java.util.WeakHashMap<K,V>

publicclassWeakHashMap<K,V>
extendsAbstractMap<K,V>
implementsMap<K,V>{}WeakHashMap与Map干系以下图:

WeakHashMap的机关函数
WeakHashMap共有4个机关函数,以下:

//默许机关函数。
WeakHashMap()

//指定“容量巨细”的机关函数
WeakHashMap(intcapacity)

//指定“容量巨细”和“加载因子”的机关函数
WeakHashMap(intcapacity,floatloadFactor)

//包括“子Map”的机关函数
WeakHashMap(Map<?extendsK,?extendsV>map)WeakHashMap的API

voidclear()
Objectclone()
booleancontainsKey(Objectkey)
booleancontainsValue(Objectvalue)
Set<Entry<K,V>>entrySet()
Vget(Objectkey)
booleanisEmpty()
Set<K>keySet()
Vput(Kkey,Vvalue)
voidputAll(Map<?extendsK,?extendsV>map)
Vremove(Objectkey)
intsize()
Collection<V>values()

第2部分WeakHashMap源码剖析
上面对WeakHashMap的源码举行申明

packagejava.util;
importjava.lang.ref.WeakReference;
importjava.lang.ref.ReferenceQueue;

publicclassWeakHashMap<K,V>
extendsAbstractMap<K,V>
implementsMap<K,V>{

//默许的初始容量是16,必需是2的幂。
privatestaticfinalintDEFAULT_INITIAL_CAPACITY=16;

//最年夜容量(必需是2的幂且小于2的30次方,传进容量过上将被这个值交换)
privatestaticfinalintMAXIMUM_CAPACITY=1<<30;

//默许加载因子
privatestaticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;

//存储数据的Entry数组,长度是2的幂。
//WeakHashMap是接纳拉链法完成的,每个Entry实质上是一个单向链表
privateEntry[]table;

//WeakHashMap的巨细,它是WeakHashMap保留的键值对的数目
privateintsize;

//WeakHashMap的阈值,用于判别是不是必要调剂WeakHashMap的容量(threshold=容量*加载因子)
privateintthreshold;

//加载因籽实际巨细
privatefinalfloatloadFactor;

//queue保留的是“已被GC扫除”的“弱援用的键”。
//弱援用和ReferenceQueue是团结利用的:假如弱援用所援用的对象被渣滓接纳,Java假造机就会把这个弱援用到场到与之联系关系的援用行列中
privatefinalReferenceQueue<K>queue=newReferenceQueue<K>();

//WeakHashMap被改动的次数
privatevolatileintmodCount;

//指定“容量巨细”和“加载因子”的机关函数
publicWeakHashMap(intinitialCapacity,floatloadFactor){
if(initialCapacity<0)
thrownewIllegalArgumentException("IllegalInitialCapacity:"+
initialCapacity);
//WeakHashMap的最年夜容量只能是MAXIMUM_CAPACITY
if(initialCapacity>MAXIMUM_CAPACITY)
initialCapacity=MAXIMUM_CAPACITY;

if(loadFactor<=0||Float.isNaN(loadFactor))
thrownewIllegalArgumentException("IllegalLoadfactor:"+
loadFactor);
//找出“年夜于initialCapacity”的最小的2的幂
intcapacity=1;
while(capacity<initialCapacity)
capacity<<=1;
//创立Entry数组,用来保留数据
table=newEntry;
//设置“加载因子”
this.loadFactor=loadFactor;
//设置“WeakHashMap阈值”,当WeakHashMap中存储数据的数目到达threshold时,就必要将WeakHashMap的容量更加。
threshold=(int)(capacity*loadFactor);
}

//指定“容量巨细”的机关函数
publicWeakHashMap(intinitialCapacity){
this(initialCapacity,DEFAULT_LOAD_FACTOR);
}

//默许机关函数。
publicWeakHashMap(){
this.loadFactor=DEFAULT_LOAD_FACTOR;
threshold=(int)(DEFAULT_INITIAL_CAPACITY);
table=newEntry;
}

//包括“子Map”的机关函数
publicWeakHashMap(Map<?extendsK,?extendsV>m){
this(Math.max((int)(m.size()/DEFAULT_LOAD_FACTOR)+1,16),
DEFAULT_LOAD_FACTOR);
//将m中的全体元素逐一增加到WeakHashMap中
putAll(m);
}

//键为null的mask值。
//由于WeakReference中同意“null的key”,若间接拔出“null的key”,将其看成弱援用时,会被删除。
//因而,这里关于“key为null”的清空,都一致交换为“key为NULL_KEY”,“NULL_KEY”是“静态的final常量”。
privatestaticfinalObjectNULL_KEY=newObject();

//对“null的key”举行特别处置
privatestaticObjectmaskNull(Objectkey){
return(key==null?NULL_KEY:key);
}

//复原对“null的key”的特别处置
privatestatic<K>KunmaskNull(Objectkey){
return(K)(key==NULL_KEY?null:key);
}

//判别“x”和“y”是不是相称
staticbooleaneq(Objectx,Objecty){
returnx==y||x.equals(y);
}

//前往索引值
//h&(length-1)包管前往值的小于length
staticintindexFor(inth,intlength){
returnh&(length-1);
}

//清空table中无用键值对。道理以下:
//(01)当WeakHashMap中某个“弱援用的key”因为没有再被援用而被GC发出时,
//被接纳的“该弱援用key”也被会被增加到"ReferenceQueue(queue)"中。
//(02)当我们实行expungeStaleEntries时,
//就遍历"ReferenceQueue(queue)"中的一切key
//然后就在“WeakReference的table”中删除与“ReferenceQueue(queue)中key”对应的键值对
privatevoidexpungeStaleEntries(){
Entry<K,V>e;
while((e=(Entry<K,V>)queue.poll())!=null){
inth=e.hash;
inti=indexFor(h,table.length);

Entry<K,V>prev=table;
Entry<K,V>p=prev;
while(p!=null){
Entry<K,V>next=p.next;
if(p==e){
if(prev==e)
table=next;
else
prev.next=next;
e.next=null;//HelpGC
e.value=null;//""
size--;
break;
}
prev=p;
p=next;
}
}
}

//猎取WeakHashMap的table(寄存键值对的数组)
privateEntry[]getTable(){
//删除table中“已被GC接纳的key对应的键值对”
expungeStaleEntries();
returntable;
}

//猎取WeakHashMap的实践巨细
publicintsize(){
if(size==0)
return0;
//删除table中“已被GC接纳的key对应的键值对”
expungeStaleEntries();
returnsize;
}

publicbooleanisEmpty(){
returnsize()==0;
}

//猎取key对应的value
publicVget(Objectkey){
Objectk=maskNull(key);
//猎取key的hash值。
inth=HashMap.hash(k.hashCode());
Entry[]tab=getTable();
intindex=indexFor(h,tab.length);
Entry<K,V>e=tab;
//在“该hash值对应的链表”上查找“键值即是key”的元素
while(e!=null){
if(e.hash==h&&eq(k,e.get()))
returne.value;
e=e.next;
}
returnnull;
}

//WeakHashMap是不是包括key
publicbooleancontainsKey(Objectkey){
returngetEntry(key)!=null;
}

//前往“键为key”的键值对
Entry<K,V>getEntry(Objectkey){
Objectk=maskNull(key);
inth=HashMap.hash(k.hashCode());
Entry[]tab=getTable();
intindex=indexFor(h,tab.length);
Entry<K,V>e=tab;
while(e!=null&&!(e.hash==h&&eq(k,e.get())))
e=e.next;
returne;
}

//将“key-value”增加到WeakHashMap中
publicVput(Kkey,Vvalue){
Kk=(K)maskNull(key);
inth=HashMap.hash(k.hashCode());
Entry[]tab=getTable();
inti=indexFor(h,tab.length);

for(Entry<K,V>e=tab;e!=null;e=e.next){
//若“该key”对应的键值对已存在,则用新的value代替旧的value。然前进出!
if(h==e.hash&&eq(k,e.get())){
VoldValue=e.value;
if(value!=oldValue)
e.value=value;
returnoldValue;
}
}

//若“该key”对应的键值对不存在于WeakHashMap中,则将“key-value”增加到table中
modCount++;
Entry<K,V>e=tab;
tab=newEntry<K,V>(k,value,queue,h,e);
if(++size>=threshold)
resize(tab.length*2);
returnnull;
}

//从头调剂WeakHashMap的巨细,newCapacity是调剂后的单元
voidresize(intnewCapacity){
Entry[]oldTable=getTable();
intoldCapacity=oldTable.length;
if(oldCapacity==MAXIMUM_CAPACITY){
threshold=Integer.MAX_VALUE;
return;
}

//新建一个newTable,将“旧的table”的全体元素增加到“新的newTable”中,
//然后,将“新的newTable”赋值给“旧的table”。
Entry[]newTable=newEntry;
transfer(oldTable,newTable);
table=newTable;

if(size>=threshold/2){
threshold=(int)(newCapacity*loadFactor);
}else{
//删除table中“已被GC接纳的key对应的键值对”
expungeStaleEntries();
transfer(newTable,oldTable);
table=oldTable;
}
}

//将WeakHashMap中的全体元素都增加到newTable中
privatevoidtransfer(Entry[]src,Entry[]dest){
for(intj=0;j<src.length;++j){
Entry<K,V>e=src;
src=null;
while(e!=null){
Entry<K,V>next=e.next;
Objectkey=e.get();
if(key==null){
e.next=null;//HelpGC
e.value=null;//""
size--;
}else{
inti=indexFor(e.hash,dest.length);
e.next=dest;
dest=e;
}
e=next;
}
}
}

//将"m"的全体元素都增加到WeakHashMap中
publicvoidputAll(Map<?extendsK,?extendsV>m){
intnumKeysToBeAdded=m.size();
if(numKeysToBeAdded==0)
return;

//盘算容量是不是充足,
//若“以后实践容量<必要的容量”,则将容量x2。
if(numKeysToBeAdded>threshold){
inttargetCapacity=(int)(numKeysToBeAdded/loadFactor+1);
if(targetCapacity>MAXIMUM_CAPACITY)
targetCapacity=MAXIMUM_CAPACITY;
intnewCapacity=table.length;
while(newCapacity<targetCapacity)
newCapacity<<=1;
if(newCapacity>table.length)
resize(newCapacity);
}

//将“m”中的元素逐一增加到WeakHashMap中。
for(Map.Entry<?extendsK,?extendsV>e:m.entrySet())
put(e.getKey(),e.getValue());
}

//删除“键为key”元素
publicVremove(Objectkey){
Objectk=maskNull(key);
//猎取哈希值。
inth=HashMap.hash(k.hashCode());
Entry[]tab=getTable();
inti=indexFor(h,tab.length);
Entry<K,V>prev=tab;
Entry<K,V>e=prev;

//删除链表中“键为key”的元素
//实质是“删除单向链表中的节点”
while(e!=null){
Entry<K,V>next=e.next;
if(h==e.hash&&eq(k,e.get())){
modCount++;
size--;
if(prev==e)
tab=next;
else
prev.next=next;
returne.value;
}
prev=e;
e=next;
}

returnnull;
}

//删除“键值对”
Entry<K,V>removeMapping(Objecto){
if(!(oinstanceofMap.Entry))
returnnull;
Entry[]tab=getTable();
Map.Entryentry=(Map.Entry)o;
Objectk=maskNull(entry.getKey());
inth=HashMap.hash(k.hashCode());
inti=indexFor(h,tab.length);
Entry<K,V>prev=tab;
Entry<K,V>e=prev;

//删除链表中的“键值对e”
//实质是“删除单向链表中的节点”
while(e!=null){
Entry<K,V>next=e.next;
if(h==e.hash&&e.equals(entry)){
modCount++;
size--;
if(prev==e)
tab=next;
else
prev.next=next;
returne;
}
prev=e;
e=next;
}

returnnull;
}

//清空WeakHashMap,将一切的元素设为null
publicvoidclear(){
while(queue.poll()!=null)
;

modCount++;
Entry[]tab=table;
for(inti=0;i<tab.length;++i)
tab=null;
size=0;

while(queue.poll()!=null)
;
}

//是不是包括“值为value”的元素
publicbooleancontainsValue(Objectvalue){
//若“value为null”,则挪用containsNullValue()查找
if(value==null)
returncontainsNullValue();

//若“value不为null”,则查找WeakHashMap中是不是有值为value的节点。
Entry[]tab=getTable();
for(inti=tab.length;i-->0;)
for(Entrye=tab;e!=null;e=e.next)
if(value.equals(e.value))
returntrue;
returnfalse;
}

//是不是包括null值
privatebooleancontainsNullValue(){
Entry[]tab=getTable();
for(inti=tab.length;i-->0;)
for(Entrye=tab;e!=null;e=e.next)
if(e.value==null)
returntrue;
returnfalse;
}

//Entry是单向链表。
//它是“WeakHashMap链式存储法”对应的链表。
//它完成了Map.Entry接口,即完成getKey(),getValue(),setValue(Vvalue),equals(Objecto),hashCode()这些函数
privatestaticclassEntry<K,V>extendsWeakReference<K>implementsMap.Entry<K,V>{
privateVvalue;
privatefinalinthash;
//指向下一个节点
privateEntry<K,V>next;

//机关函数。
Entry(Kkey,Vvalue,
ReferenceQueue<K>queue,
inthash,Entry<K,V>next){
super(key,queue);
this.value=value;
this.hash=hash;
this.next=next;
}

publicKgetKey(){
returnWeakHashMap.<K>unmaskNull(get());
}

publicVgetValue(){
returnvalue;
}

publicVsetValue(VnewValue){
VoldValue=value;
value=newValue;
returnoldValue;
}

//判别两个Entry是不是相称
//若两个Entry的“key”和“value”都相称,则前往true。
//不然,前往false
publicbooleanequals(Objecto){
if(!(oinstanceofMap.Entry))
returnfalse;
Map.Entrye=(Map.Entry)o;
Objectk1=getKey();
Objectk2=e.getKey();
if(k1==k2||(k1!=null&&k1.equals(k2))){
Objectv1=getValue();
Objectv2=e.getValue();
if(v1==v2||(v1!=null&&v1.equals(v2)))
returntrue;
}
returnfalse;
}

//完成hashCode()
publicinthashCode(){
Objectk=getKey();
Objectv=getValue();
return((k==null?0:k.hashCode())^
(v==null?0:v.hashCode()));
}

publicStringtoString(){
returngetKey()+"="+getValue();
}
}

//HashIterator是WeakHashMap迭代器的笼统出来的父类,完成了大众了函数。
//它包括“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。
privateabstractclassHashIterator<T>implementsIterator<T>{
//以后索引
intindex;
//以后元素
Entry<K,V>entry=null;
//上一次前往元素
Entry<K,V>lastReturned=null;
//expectedModCount用于完成fast-fail机制。
intexpectedModCount=modCount;

//下一个键(强援用)
ObjectnextKey=null;

//以后键(强援用)
ObjectcurrentKey=null;

//机关函数
HashIterator(){
index=(size()!=0?table.length:0);
}

//是不是存鄙人一个元素
publicbooleanhasNext(){
Entry[]t=table;

//一个Entry就是一个单向链表
//若该Entry的下一个节点不为空,就将next指向下一个节点;
//不然,将next指向下一个链表(也是下一个Entry)的不为null的节点。
while(nextKey==null){
Entry<K,V>e=entry;
inti=index;
while(e==null&&i>0)
e=t[--i];
entry=e;
index=i;
if(e==null){
currentKey=null;
returnfalse;
}
nextKey=e.get();//holdontokeyinstrongref
if(nextKey==null)
entry=entry.next;
}
returntrue;
}

//猎取下一个元素
protectedEntry<K,V>nextEntry(){
if(modCount!=expectedModCount)
thrownewConcurrentModificationException();
if(nextKey==null&&!hasNext())
thrownewNoSuchElementException();

lastReturned=entry;
entry=entry.next;
currentKey=nextKey;
nextKey=null;
returnlastReturned;
}

//删除以后元素
publicvoidremove(){
if(lastReturned==null)
thrownewIllegalStateException();
if(modCount!=expectedModCount)
thrownewConcurrentModificationException();

WeakHashMap.this.remove(currentKey);
expectedModCount=modCount;
lastReturned=null;
currentKey=null;
}

}

//value的迭代器
privateclassValueIteratorextendsHashIterator<V>{
publicVnext(){
returnnextEntry().value;
}
}

//key的迭代器
privateclassKeyIteratorextendsHashIterator<K>{
publicKnext(){
returnnextEntry().getKey();
}
}

//Entry的迭代器
privateclassEntryIteratorextendsHashIterator<Map.Entry<K,V>>{
publicMap.Entry<K,V>next(){
returnnextEntry();
}
}

//WeakHashMap的Entry对应的汇合
privatetransientSet<Map.Entry<K,V>>entrySet=null;

//前往“key的汇合”,实践上前往一个“KeySet对象”
publicSet<K>keySet(){
Set<K>ks=keySet;
return(ks!=null?ks:(keySet=newKeySet()));
}

//Key对应的汇合
//KeySet承继于AbstractSet,申明该汇合中没有反复的Key。
privateclassKeySetextendsAbstractSet<K>{
publicIterator<K>iterator(){
returnnewKeyIterator();
}

publicintsize(){
returnWeakHashMap.this.size();
}

publicbooleancontains(Objecto){
returncontainsKey(o);
}

publicbooleanremove(Objecto){
if(containsKey(o)){
WeakHashMap.this.remove(o);
returntrue;
}
else
returnfalse;
}

publicvoidclear(){
WeakHashMap.this.clear();
}
}

//前往“value汇合”,实践上前往的是一个Values对象
publicCollection<V>values(){
Collection<V>vs=values;
return(vs!=null?vs:(values=newValues()));
}

//“value汇合”
//Values承继于AbstractCollection,分歧于“KeySet承继于AbstractSet”,
//Values中的元素可以反复。由于分歧的key能够指向不异的value。
privateclassValuesextendsAbstractCollection<V>{
publicIterator<V>iterator(){
returnnewValueIterator();
}

publicintsize(){
returnWeakHashMap.this.size();
}

publicbooleancontains(Objecto){
returncontainsValue(o);
}

publicvoidclear(){
WeakHashMap.this.clear();
}
}

//前往“WeakHashMap的Entry汇合”
//它实践是前往一个EntrySet对象
publicSet<Map.Entry<K,V>>entrySet(){
Set<Map.Entry<K,V>>es=entrySet;
returnes!=null?es:(entrySet=newEntrySet());
}

//EntrySet对应的汇合
//EntrySet承继于AbstractSet,申明该汇合中没有反复的EntrySet。
privateclassEntrySetextendsAbstractSet<Map.Entry<K,V>>{
publicIterator<Map.Entry<K,V>>iterator(){
returnnewEntryIterator();
}

//是不是包括“值(o)”
publicbooleancontains(Objecto){
if(!(oinstanceofMap.Entry))
returnfalse;
Map.Entrye=(Map.Entry)o;
Objectk=e.getKey();
Entrycandidate=getEntry(e.getKey());
returncandidate!=null&&candidate.equals(e);
}

//删除“值(o)”
publicbooleanremove(Objecto){
returnremoveMapping(o)!=null;
}

//前往WeakHashMap的巨细
publicintsize(){
returnWeakHashMap.this.size();
}

//清空WeakHashMap
publicvoidclear(){
WeakHashMap.this.clear();
}

//拷贝函数。将WeakHashMap中的全体元素都拷贝到List中
privateList<Map.Entry<K,V>>deepCopy(){
List<Map.Entry<K,V>>list=newArrayList<Map.Entry<K,V>>(size());
for(Map.Entry<K,V>e:this)
list.add(newAbstractMap.SimpleEntry<K,V>(e));
returnlist;
}

//前往Entry对应的Object[]数组
publicObject[]toArray(){
returndeepCopy().toArray();
}

//前往Entry对应的T[]数组(T[]我们新建数组时,界说的数组范例)
public<T>T[]toArray(T[]a){
returndeepCopy().toArray(a);
}
}
}检察本栏目更多出色内容:http://www.bianceng.cn/Programming/Java/
申明:
<p>
对于一个大型项目,如果用java来作,可能需要9个月,并且可能需要翻阅10本以上的书,但如果用ruby来作,3个月,3本书就足够了,而.net也不过3,4本书足以,这就是区别。

透明 发表于 2015-1-20 12:34:29

其实说这种话的人就如当年小日本号称“三个月拿下中国”一样大言不惭。不是Tomjava泼你冷水,你现在只是学到了Java的骨架,却还没有学到Java的精髓。接下来你得研究设计模式了。

金色的骷髅 发表于 2015-1-24 16:08:35

当然你也可以参加一些开源项目,一方面可以提高自己,另一方面也是为中国软件事业做贡献嘛!开发者在互联网上用CVS合作开发,用QQ,MSN,E-mail讨论联系,天南海北的程序员分散在各地却同时开发同一个软件,是不是很有意思呢?

深爱那片海 发表于 2015-1-30 17:37:54

应用在电视机、电话、闹钟、烤面包机等家用电器的控制和通信。由于这些智能化家电的市场需求没有预期的高,Sun公司放弃了该项计划。随着1990年代互联网的发展

只想知道 发表于 2015-2-6 14:34:18

是一种突破用户端机器环境和CPU

莫相离 发表于 2015-2-16 13:57:49

J2SE开发桌面应用软件比起 VC,VB,DEPHI这些传统开发语言来说,优势好象并不明显。J2ME对于初学者来说,好象又有点深奥,而且一般开发者很难有开发环境。

老尸 发表于 2015-3-5 06:06:09

是一种突破用户端机器环境和CPU

柔情似水 发表于 2015-3-11 23:37:44

Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。

若天明 发表于 2015-3-19 16:10:07

不过,每次的执行编译后的字节码需要消耗一定的时间,这同时也在一定程度上降低了 Java 程序的运行效率。

爱飞 发表于 2015-3-29 00:53:14

Java 不同于一般的编译执行计算机语言和解释执行计算机语言。它首先将源代码编译成二进制字节码(bytecode),然后依赖各种不同平台上的虚拟机来解释执行字节码。从而实现了“一次编译、到处执行”的跨平台特性。
页: [1]
查看完整版本: 发布一篇Java汇合进修(十三) WeakHashMap具体先容(源码剖析)和利用示例 ...