hashmap的初始容量和装载因子Word格式.docx

上传人:b****4 文档编号:18216184 上传时间:2022-12-14 格式:DOCX 页数:4 大小:18.92KB
下载 相关 举报
hashmap的初始容量和装载因子Word格式.docx_第1页
第1页 / 共4页
hashmap的初始容量和装载因子Word格式.docx_第2页
第2页 / 共4页
hashmap的初始容量和装载因子Word格式.docx_第3页
第3页 / 共4页
hashmap的初始容量和装载因子Word格式.docx_第4页
第4页 / 共4页
亲,该文档总共4页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

hashmap的初始容量和装载因子Word格式.docx

《hashmap的初始容量和装载因子Word格式.docx》由会员分享,可在线阅读,更多相关《hashmap的初始容量和装载因子Word格式.docx(4页珍藏版)》请在冰豆网上搜索。

hashmap的初始容量和装载因子Word格式.docx

_____________________________________________

//HashMap的构造

publicHashMap(intinitialCapacity,floatloadFactor){ 

if(initialCapacity<

0) 

thrownewIllegalArgumentException("

Illegalinitialcapacity:

"

initialCapacity);

if(initialCapacity>

MAXIMUM_CAPACITY) 

initialCapacity=MAXIMUM_CAPACITY;

if(loadFactor<

=0||Float.isNaN(loadFactor)) 

Illegalloadfactor:

loadFactor);

//Findapowerof2>

=initialCapacity 

intcapacity=1;

while(capacity<

initialCapacity) 

capacity<

<

=1;

this.loadFactor=loadFactor;

threshold=(int)(capacity*loadFactor);

table=newEntry[capacity];

init();

}

publicV 

put(Kkey,Vvalue){ 

if(key==null) 

returnputForNullKey(value);

inthash=hash(key.hashCode());

inti=indexFor(hash,table.length);

for(Entry<

K,V>

e=table[i];

e!

=null;

e=e.next){ 

Objectk;

if(e.hash==hash&

&

((k=e.key)==key||key.equals(k))){ 

VoldValue=e.value;

e.value=value;

e.recordAccess(this);

returnoldValue;

modCount++;

addEntry(hash,key,value,i);

returnnull;

//添加新的项目voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){ 

Entry<

e=table[bucketIndex];

table[bucketIndex]=newEntry<

(hash,key,value,e);

if(size++>

=threshold) 

//注意理解这里,当前的实际大小(size)与threshold相等时,就将当前的容量扩大一倍 

 

resize(2*table.length);

//重构大小voidresize(intnewCapacity){ 

Entry[]oldTable=table;

intoldCapacity=oldTable.length;

if(oldCapacity==MAXIMUM_CAPACITY){ 

threshold=Integer.MAX_VALUE;

return;

Entry[]newTable=newEntry[newCapacity];

transfer(newTable);

table=newTable;

threshold=(int)(newCapacity*loadFactor);

HashMap的数据结构 

HashMap主要是用数组来存储数据的,我们都知道它会对key进行哈希运算,哈系运算会有重复的哈希值,对于哈希值的冲突,HashMap采用链表来解决的。

在HashMap里有这样的一句属性声明:

transientEntry[]table;

Entry就是HashMap存储数据所用的类,它拥有的属性如下finalKkey;

Vvalue;

finalinthash;

next;

看到next了吗?

next就是为了哈希冲突而存在的。

比如通过哈希运算,一个新元素应该在数组的第10个位置,但是第10个位置已经有Entry,那么好吧,将新加的元素也放到第10个位置,将第10个位置的原有Entry赋值给当前新加的Entry的next属性。

数组存储的是链表,链表是为了解决哈希冲突的,这一点要注意。

几个关键的属性存储数据的数组transientEntry[]table;

这个上面已经讲到了默认容量staticfinalintDEFAULT_INITIAL_CAPACITY=16;

最大容量staticfinalintMAXIMUM_CAPACITY=1<

30;

默认加载因子,加载因子是一个比例,当HashMap的数据大小>

=容量*加载因子时,HashMap会将容量扩容staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;

当实际数据大小超过threshold时,HashMap会将容量扩容,threshold=容量*加载因子intthreshold;

加载因子finalfloatloadFactor;

HashMap的初始过程构造函数1 

intcapacity=1;

threshold=(int)(capacity*loadFactor);

}重点注意这里 

while(capacity<

capacity才是初始容量,而不是initialCapacity,这个要特别注意,如果执行newHashMap(9,0.75);

那么HashMap的初始容量是16,而不是9,想想为什么吧。

构造函数2publicHashMap(intinitialCapacity){ 

this(initialCapacity,DEFAULT_LOAD_FACTOR);

}构造函数3,全部都是默认值 

publicHashMap(){ 

this.loadFactor=DEFAULT_LOAD_FACTOR;

threshold=(int)(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR);

table=newEntry[DEFAULT_INITIAL_CAPACITY];

}构造函数4 

publicHashMap(Map<

?

extendsK,?

extendsV>

m){ 

this(Math.max((int)(m.size()/DEFAULT_LOAD_FACTOR)+1, 

DEFAULT_INITIAL_CAPACITY),DEFAULT_LOAD_FACTOR);

putAllForCreate(m);

}如何哈希 

HashMap并不是直接将对象的hashcode作为哈希值的,而是要把key的hashcode作一些运算以得到最终的哈希值,并且得到的哈希值也不是在数组中的位置哦,无论是get还是put还是别的方法,计算哈希值都是这一句:

inthash=hash(key.hashCode());

hash函数如下:

staticinthash(inth){ 

returnuseNewHash?

newHash(h):

oldHash(h);

}useNewHash声明如下:

privatestaticfinalbooleanuseNewHash;

static{useNewHash=false;

}这说明useNewHash其实一直为false且不可改变的,hash函数里对useNewHash的判断真是多余的。

privatestaticintoldHash(inth){ 

h+=~(h<

9);

h^= 

(h>

>

14);

h+= 

(h<

4);

10);

returnh;

privatestaticintnewHash(inth){ 

//ThisfunctionensuresthathashCodesthatdifferonlyby 

//constantmultiplesateachbitpositionhaveabounded 

//numberofcollisions(approximately8atdefaultloadfactor). 

h^=(h>

20)^(h>

12);

returnh^(h>

7)^(h>

}其实HashMap的哈希函数会一直都是oldHash。

如果确定数据的位置看下面两行 

inthash=hash(k.hashCode());

第一行,上面讲过了,是得到哈希值,第二行,则是根据哈希指计算元素在数组中的位置了,位置的计算是将哈希值和数组长度按位与运算。

staticintindexFor(inth,intlength){ 

returnh&

(length-1);

}put方法到底作了什么?

publicVput(Kkey,Vvalue){ 

addEntry(hash,key,value,i);

}如果key为NULL,则是单独处理的,看看putForNullKey方法:

privateVputForNullKey(Vvalue){ 

inthash=hash(NULL_KEY.hashCode());

if(e.key==NULL_KEY){ 

addEntry(hash,(K)NULL_KEY,value,i);

}NULL_KEY的声明:

staticfinalObjectNULL_KEY=newObject();

这一段代码是处理哈希冲突的,就是说,在数组某个位置的对象可能并不是唯一的,它是一个链表结构,根据哈希值找到链表后,还要对链表遍历,找出key相等的对象,替换它,并且返回旧的值。

}如果遍历完了该位置的链表都没有找到有key相等的,那么将当前对象增加到链表里面去 

且看看addEntry方法 

voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){ 

Entry<

table[bucketIndex]=newEntry<

resize(2*table.length);

}table[bucketIndex]=newEntry<

新建一个Entry对象,并放在当前位置的Entry链表的头部,看看下面的Entry构造函数就知道了,注意红色部分。

Entry(inth,Kk,Vv,Entry<

n){ 

value=v;

next=n;

key=k;

hash=h;

}如何扩容?

当put一个元素时,如果达到了容量限制,HashMap就会扩容,新的容量永远是原来的2倍。

上面的put方法里有这样的一段:

if(size++>

这是扩容判断,要注意,并不是数据尺寸达到HashMap的最大容量时才扩容,而是达到threshold指定的值时就开始扩容,threshold=最大容量*加载因子。

看看resize方法 

voidresize(intnewCapacity){ 

transfer(newTable);

}重点看看红色部分的transfer方法 

voidtransfer(Entry[]newTable){ 

Entry[]src=table;

intnewCapacity=newTable.length;

for(intj=0;

j<

src.length;

j++){ 

e=src[j];

if(e!

=null){ 

src[j]=null;

do{ 

next=e.next;

inti=indexFor(e.hash,newCapacity);

e.next=newTable[i];

newTable[i]=e;

e=next;

}while(e!

=null);

}tranfer方法将所有的元素重新哈希,因为新的容量变大,所以每个元素的哈希值和位置都是不一样的。

正确的使用HashMap1:

不要在并发场景中使用HashMap 

HashMap是线程不安全的,如果被多个线程共享的操作,将会引发不可预知的问题,据sun的说法,在扩容时,会引起链表的闭环,在get元素时,就会无限循环,后果是cpu100%。

看看get方法的红色部分publicVget(Objectkey){ 

returngetForNullKey();

e=table[indexFor(hash,table.length)];

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 化学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1