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

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

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

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

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

hashmap的初始容量和装载因子

HashMap的初始容量(initialCapacity)和装载因子(loadFactor)

按HashMap源码里的那种重构方法,如果reHash过多,显然会影响性能。

所以为了防止过多的reHash,我们需要自己配置HashMap的装载因子loadFactor和初始的table容量capacity的大小(可以在构造函数里配或者调用方法配)。

 很容易理解,如果我们已经知道我们使用的HashMap一般情况的存储在1W对以上,你给它一个默认的16的初始的table容量,默认reHash每次容量翻倍,这得重构多少次呀!

(如果装载因子为1,还得要约5~6次)。

但是如果我们的对HashMap的容量需求不是很大,你给它一个默认1W的容量,显然又浪费宝贵的空间了。

至于这两个参数的选择可以自己去把握,甚至可以设定动态绑定:

分析历史数据,找出规律,或者预测未来的走向找出规律。

对HashMap这两个参数实现一个动态的调整。

比如早上8点~9点A业务比较忙,它对应的HashMap可以提前多给些空间,而10点以后B业务使用的HashMap比较忙,A相对清闲,可以缩减A的空间给B。

如果从数据库的表中读取记录存入HashMap中,完全可以根据记录的行数(rowsize)来初始化HashMap的容量,这样就可以达到reHash的最少次数,同时也保证了HashMap所需的最小容量:

比如:

通过SQL语句:

selectcount(字段)asrowSizefrom表

提到行数:

 rowSize=30

那么我们在定义HashMap的时候:

  HashMaph=newHashMap(rowSize,1f);

 

下面是HashMap的相关源代码部分:

_____________________________________________

//HashMap的构造

publicHashMap(intinitialCapacity,floatloadFactor){       if(initialCapacity<0)           thrownewIllegalArgumentException("Illegalinitialcapacity:

"+                                              initialCapacity);       if(initialCapacity>MAXIMUM_CAPACITY)           initialCapacity=MAXIMUM_CAPACITY;       if(loadFactor<=0||Float.isNaN(loadFactor))           thrownewIllegalArgumentException("Illegalloadfactor:

"+                                              loadFactor);

       //Findapowerof2>=initialCapacity        intcapacity=1;       while(capacity

       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(Entrye=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){ Entrye=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;Entrynext;

看到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    publicHashMap(intinitialCapacity,floatloadFactor){       if(initialCapacity<0)           thrownewIllegalArgumentException("Illegalinitialcapacity:

"+                                              initialCapacity);       if(initialCapacity>MAXIMUM_CAPACITY)           initialCapacity=MAXIMUM_CAPACITY;       if(loadFactor<=0||Float.isNaN(loadFactor))           thrownewIllegalArgumentException("Illegalloadfactor:

"+                                              loadFactor);       //Findapowerof2>=initialCapacity       intcapacity=1;       while(capacity

构造函数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];       init();   }构造函数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);       h^= (h>>>10);       returnh;   }   privatestaticintnewHash(inth){       //ThisfunctionensuresthathashCodesthatdifferonlyby       //constantmultiplesateachbitpositionhaveabounded       //numberofcollisions(approximately8atdefaultloadfactor).       h^=(h>>>20)^(h>>>12);       returnh^(h>>>7)^(h>>>4);   }其实HashMap的哈希函数会一直都是oldHash。

如果确定数据的位置看下面两行      inthash=hash(k.hashCode());     inti=indexFor(hash,table.length);第一行,上面讲过了,是得到哈希值,第二行,则是根据哈希指计算元素在数组中的位置了,位置的计算是将哈希值和数组长度按位与运算。

   staticintindexFor(inth,intlength){       returnh&(length-1);   }put方法到底作了什么?

   publicVput(Kkey,Vvalue){   if(key==null)       returnputForNullKey(value);       inthash=hash(key.hashCode());       inti=indexFor(hash,table.length);       for(Entrye=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;   }如果key为NULL,则是单独处理的,看看putForNullKey方法:

   privateVputForNullKey(Vvalue){       inthash=hash(NULL_KEY.hashCode());       inti=indexFor(hash,table.length);       for(Entrye=table[i];e!

=null;e=e.next){           if(e.key==NULL_KEY){               VoldValue=e.value;               e.value=value;               e.recordAccess(this);               returnoldValue;           }       }       modCount++;       addEntry(hash,(K)NULL_KEY,value,i);       returnnull;   }NULL_KEY的声明:

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

     for(Entrye=table[i];e!

=null;e=e.next){           if(e.key==NULL_KEY){               VoldValue=e.value;               e.value=value;               e.recordAccess(this);               returnoldValue;           }       }如果遍历完了该位置的链表都没有找到有key相等的,那么将当前对象增加到链表里面去 modCount++; addEntry(hash,(K)NULL_KEY,value,i); returnnull;且看看addEntry方法   voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){   Entrye=table[bucketIndex];        table[bucketIndex]=newEntry(hash,key,value,e);       if(size++>=threshold)           resize(2*table.length);   }table[bucketIndex]=newEntry(hash,key,value,e);新建一个Entry对象,并放在当前位置的Entry链表的头部,看看下面的Entry构造函数就知道了,注意红色部分。

    Entry(inth,Kk,Vv,Entryn){           value=v;           next=n;           key=k;           hash=h;       }如何扩容?

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

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

if(size++>=threshold)           resize(2*table.length);这是扩容判断,要注意,并不是数据尺寸达到HashMap的最大容量时才扩容,而是达到threshold指定的值时就开始扩容,threshold=最大容量*加载因子。

看看resize方法   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);   }重点看看红色部分的transfer方法    voidtransfer(Entry[]newTable){       Entry[]src=table;       intnewCapacity=newTable.length;       for(intj=0;je=src[j];           if(e!

=null){               src[j]=null;               do{                   Entrynext=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){   if(key==null)       returngetForNullKey();       inthash=hash(key.hashCode());       for(Entrye=table[indexFor(hash,table.length)];      

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

当前位置:首页 > 小学教育 > 英语

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

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