2012年软考软件设计师辅导:JavaHashMap分析基本结构

来源:软件水平考试    发布时间:2012-11-04    软件水平考试视频    评论

  Java的HashMap非常的常用,本篇研究它的实现算法,最后希望计算出内存占用,性能的量化数据,然后得出什么时候使用HashMap,什么时候不能滥用的结论。

  HashMap实际上是一个数组,数组里面的每个元素都是一个链表。每个元素在通过put方法放入HashMap中的时候,要按照如下步骤进行:1.根据该元素自身提供的hashcode计算出散列值,该散列值就是数组的下标2.将新元素放入该数组位置的链表中先来看一下数组的定义:[java]viewplaincopy/***Thetable,resizedasnecessary.LengthMUSTAlwaysbeapoweroftwo.*/transientEntry[]table;

  这是一个数组,transient关键字告诉我们它不会参与序列化。既然是一个数组,总有数目上限,也就意味着如果存入HashMap的元素太多,导致数组大小不能够存放所有的链表的时候,数组大小必须要能够调整。所以首先来考察一下数组容量的相关算法。

  第一,Entry是什么类型?

   [java]viewplaincopystaticclassEntry<K,V>implementsMap.Entry<K,V>{finalKkey;Vvalue;Entry<K,V>next;finalinthash;

   /***Createsnewentry.*/Entry(inth,Kk,Vv,Entry<K,V>n){value=v;next=n;key=k;hash=h;}……

   publicfinalbooleanequals(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;}

   publicfinalinthashCode(){return(key==null?0:key.hashCode())^(value==null?0:value.hashCode());}……

  这是一个HashMap类的内部静态类。实现了Map.Entry接口。接受两个模板参数K和V.key和hash一旦在构造函数中被初始化,就不可改变,并且由于有next的存在,Entry可以构成一个单向链表。

  比较重要的是equals和hashCode方法。代码先列出来,后面再解释。

  第二,初始容量的设定大多数都在下面的构造函数里面。用于指定的initialCapacity不准小于0,也不能超过最大值。并且最终的capicity必须是2的n次方。还有如果使用了无参数的构造函数,默认会创建一个拥有16个元素的数组。

   [java]viewplaincopypublicHashMap(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>=initialCapacityintcapacity=1;while(capacity<initialCapacity)

   capacity<<=1;

   this.loadFactor=loadFactor;threshold=(int)(capacity*loadFactor);table=newEntry[capacity];init();}

  第三,什么时候应该调整数组的大小?

  算法是这样,有一个变量size保存了实际数组已经使用了多少个元素,并且如果size的值达到了变量threshold的值,就必须扩充数组的容量。threshold=capicity*loadFactor.capicity是数组最大的容纳元素个数,loadFactor可以在构造函数中制定,否则采用默认值0.75f.capicity的最大值是1<<30(也就是2的30次方,1073741824)。由此我们可以看到HashMap最多存放10亿多个链表。

  第四,如何调整数组大小?

  答案是2倍,很像C++里面的vector的分配策略。

   [java]viewplaincopyvoidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){Entry<K,V>e=table[bucketIndex];table[bucketIndex]=newEntry<K,V>(hash,key,value,e);if(size++>=threshold)

   resize(2*table.length);}

  第五,为什么数组大小必须是2的倍数?

  在后面介绍散列值算法的时候会回答。

视频学习

我考网版权与免责声明

① 凡本网注明稿件来源为"原创"的所有文字、图片和音视频稿件,版权均属本网所有。任何媒体、网站或个人转载、链接转贴或以其他方式复制发表时必须注明"稿件来源:我考网",违者本网将依法追究责任;

② 本网部分稿件来源于网络,任何单位或个人认为我考网发布的内容可能涉嫌侵犯其合法权益,应该及时向我考网书面反馈,并提供身份证明、权属证明及详细侵权情况证明,我考网在收到上述法律文件后,将会尽快移除被控侵权内容。

最近更新

社区交流

考试问答