Java源码分析HashTable源码分析Word文件下载.docx
《Java源码分析HashTable源码分析Word文件下载.docx》由会员分享,可在线阅读,更多相关《Java源码分析HashTable源码分析Word文件下载.docx(14页珍藏版)》请在冰点文库上搜索。
publicHashtable(){
this(11,0.75f);
}
publicHashtable(intinitialCapacity){
this(initialCapacity,0.75f);
publicHashtable(intinitialCapacity,floatloadFactor){
if(initialCapacity<
0)
thrownewIllegalArgumentException("
IllegalCapacity:
"
+
initialCapacity);
if(loadFactor<
=0||Float.isNaN(loadFactor))
IllegalLoad:
+loadFactor);
if(initialCapacity==0)
initialCapacity=1;
this.loadFactor=loadFactor;
table=newEntry[initialCapacity];
threshold=(int)Math.min(initialCapacity*loadFactor,MAX_ARRAY_SIZE+1);
useAltHashing=sun.misc.VM.isBooted()&
&
(initialCapacity>
=Holder.ALTERNATIVE_HASHING_THRESHOLD);
publicHashtable(Map<
?
extendsK,?
extendsV>
t){
this(Math.max(2*t.size(),11),0.75f);
putAll(t);
构造函数和HashMap一样,不同点在于默认容量不一样,HashTable的默认容量是11
hash函数
transientfinalinthashSeed=sun.misc.Hashing.randomHashSeed(this);
privateinthash(Objectk){
if(useAltHashing){
if(k.getClass()==String.class){
returnsun.misc.Hashing.stringHash32((String)k);
}else{
inth=hashSeed^k.hashCode();
//ThisfunctionensuresthathashCodesthatdifferonlyby
//constantmultiplesateachbitpositionhaveabounded
//numberofcollisions(approximately8atdefaultloadfactor).
h^=(h>
>
20)^(h>
12);
returnh^(h>
7)^(h>
4);
}
returnk.hashCode();
hash的计算和HashMap是一样的
判断是否存在给定值
publicsynchronizedbooleancontains(Objectvalue){
if(value==null){
thrownewNullPointerException();
Entrytab[]=table;
for(inti=tab.length;
i-->
0;
){
for(Entry<
e=tab[i];
e!
=null;
e=e.next){
if(e.value.equals(value)){
returntrue;
returnfalse;
publicbooleancontainsValue(Objectvalue){
returncontains(value);
可以看到这里一旦发现value是null就直接抛出异常,这样直接说明HashTable是不允许null-value的。
另外该方法虽然是contains(),但是实际功能和containsValue()是一样的,从containsValue()的实现可以看出
给定的key是否存在
publicsynchronizedbooleancontainsKey(Objectkey){
inthash=hash(key);
intindex=(hash&
0x7FFFFFFF)%tab.length;
e=tab[index];
if((e.hash==hash)&
e.key.equals(key)){
当且仅当给定的key是存在的时候才返回true,但是注意一点的是这里所说的是否存在是取决于equals()方法是如何实现的。
这和开头所强调的存入HashTable的key必须实现hashCode和equals方法是一致的.这里(hash&
0x7FFFFFFF)的目的应该是为了保证hash是一个正值,毕竟只有第32位为0其他都是1
使用给定的key查找
publicsynchronizedVget(Objectkey){
returne.value;
returnnull;
同HashMap,因为这里的底层实现结构都是一样的,都是基于数组,数组的每个下标对应一个单链表。
如果对应的key在HashTable中存在着对应的value那么返回找到的value,否则返回Null
扩容
protectedvoidrehash(){
intoldCapacity=table.length;
Entry<
[]oldMap=table;
//overflow-consciouscode
intnewCapacity=(oldCapacity<
<
1)+1;
if(newCapacity-MAX_ARRAY_SIZE>
0){
if(oldCapacity==MAX_ARRAY_SIZE)
//KeeprunningwithMAX_ARRAY_SIZEbuckets
return;
newCapacity=MAX_ARRAY_SIZE;
[]newMap=newEntry[newCapacity];
modCount++;
threshold=(int)Math.min(newCapacity*loadFactor,MAX_ARRAY_SIZE+1);
booleancurrentAltHashing=useAltHashing;
(newCapacity>
booleanrehash=currentAltHashing^useAltHashing;
table=newMap;
for(inti=oldCapacity;
old=oldMap[i];
old!
){
e=old;
old=old.next;
if(rehash){
e.hash=hash(e.key);
intindex=(e.hash&
0x7FFFFFFF)%newCapacity;
e.next=newMap[index];
newMap[index]=e;
当实际装入的键值对的个数超过了table的长度也就是容量capacity和装载因子的乘积,就会进行扩容,扩容过程是自动的,但是比较耗时。
扩增后的容量是最初的容量的2倍加1,这里和HashMap对比就很容易理解,HashMap的初始容量是偶数,而且要求容量一直是偶数,那么扩容的时候就直接扩大到原来的2倍。
相应的,HashTable的初始容量是奇数11,2倍之后变成偶数,所以加1。
不过这里没有要求HashTable的容量必须是奇数,但无论如何扩容后容量都会变成奇数
添加键值对
publicsynchronizedVput(Kkey,Vvalue){
//Makesurethevalueisnotnull
//Makessurethekeyisnotalreadyinthehashtable.
Vold=e.value;
e.value=value;
returnold;
if(count>
=threshold){
//Rehashthetableifthethresholdisexceeded
rehash();
tab=table;
hash=hash(key);
index=(hash&
//Createsthenewentry.
e=tab[index];
tab[index]=newEntry<
(hash,key,value,e);
count++;
注意键和值都必须不能为NULL添加过程中如果发现给定的key已经存在,那么替换旧值并返回旧值,否则将新的键值对加入到HashTable,其中如果添加过程中发现实际键值对数目已经超过阈值,那么就进行扩容
删除键值对
publicsynchronizedVremove(Objectkey){
e=tab[index],prev=null;
prev=e,e=e.next){
if(prev!
=null){
prev.next=e.next;
tab[index]=e.next;
count--;
VoldValue=e.value;
e.value=null;
returnoldValue;
如果找到了给定key对应的键值对,那么就做删除操作,如果没有找到,那么就什么也不做。
实际的删除过程就是单链表的节点删除
清空操作
publicsynchronizedvoidclear(){
for(intindex=tab.length;
--index>
=0;
)
tab[index]=null;
count=0;
和HashMap的清空操作一样,只是将每个slot置为NULL,剩下的交给GC
比较两个HashTable是否相等
publicsynchronizedbooleanequals(Objecto){
if(o==this)
if(!
(oinstanceofMap))
Map<
t=(Map<
)o;
if(t.size()!
=size())
try{
Iterator<
Map.Entry<
i=entrySet().iterator();
while(i.hasNext()){
Map.Entry<
e=i.next();
Kkey=e.getKey();
Vvalue=e.getValue();
(t.get(key)==null&
t.containsKey(key)))
value.equals(t.get(key)))
}catch(ClassCastExceptionunused){
returnlse;
}catch(NullPointerExceptionunused){
首先比较大小是否相等,如果大小相等,那么就依次比较实体集合中每个实体的是否是一样的键值对,如果所有的键值对都一样,那么就返回true,否则返回false,注意由于HashTable中数据存储并不是有序的,所以实际比较的时候使用的是get()根据指定的key获取对应的value
获取HashTable的hashCode
publicsynchronizedinthashCode(){
/*
*Thiscodedetectstherecursioncausedbycomputingthehashcode
*ofaself-referentialhashtableandpreventsthestackoverflow
*thatwouldotherwiseresult.Thisallowscertain1.1-era
*appletswithself-referentialhashtablestowork.Thiscode
*abusestheloadFactorfieldtododouble-dutyasahashCode
*inprogressflag,soasnottoworsenthespaceperformance.
*Anegativeloadfactorindicatesthathashcodecomputationis
*inprogress.
*/
inth=0;
if(count==0||loadFactor<
returnh;
//Returnszero
loadFactor=-loadFactor;
//MarkhashCodecomputationinprogress
Entry[]tab=table;
entry:
tab)
while(entry!
h+=entry.hashCode();
entry=entry.next;
//MarkhashCodecomputationcomplete
实际计算HashTable的hashCode是根据每个实体的hashCode来计算的,所有实体的hashCode()之和就是HashTable的hashCode。
loadFractor扮演了一个标志位的角色,标志计算的开始和结束。
关于这里h+=entry.hashCode()中h是否会溢出的问题,上面再hash()函数中已经看到了每次求hash的时候都会与0x7fffffff,也就是说hash永远为正,不会溢出
序列化和反序列化
privatevoidwriteObject(java.io.ObjectOutputStreams)
throwsIOException{
K,V>
entryStack=null;
synchronized(this){
//Writeoutthelength,threshold,loadfactor
s.defaultWriteObject();
//Writeoutlength,countofelements
s.writeInt(table.length);
s.writeInt(count);
//Stackcopiesoftheentriesinthetable
for(intindex=0;
index<
table.length;
index++){
entry=table[index];
entryStack=
newEntry<
(0,entry.key,entry.value,entryStack);
//Writeoutthekey/valueobjectsfromthestackedentries
while(entryStack!
s.writeObject(entryStack.key);
s.writeObject(entryStack.value);
entryStack=entryStack.next;
privatevoidreadObject(java.io.ObjectInputStreams)
throwsIOException,ClassNotFoundException
{
//Readinthelength,threshold,andloadfactor
s.defaultReadObject();
//sethashSeed
UNSAFE.putIntVolatile(this,HASHSEED_OFFSET,
sun.misc.Hashing.randomHashSeed(this));
//Readtheoriginallengthofthearrayandnumberofelements
intoriglength=s.readInt();
intelements=s.readInt();
//Computenewsizewithabitofroom5%togrowbut
//nolargerthantheoriginalsize.Makethelength
//oddifit'
slargeenough,thishelpsdistributetheentries.