2012年软考软件设计师辅导:java中HashMap详解

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

  上面程序中使用的table其实就是一个普通数组,每个数组都有一个固定的长度,这个数组的长度就是HashMap的容量。HashMap包含如下几个构造器:

   *HashMap():构建一个初始容量为16,负载因子为0.75的HashMap。

   *HashMap(intinitialCapacity):构建一个初始容量为initialCapacity,负载因子为0.75的HashMap。

   *HashMap(intinitialCapacity,floatloadFactor):以指定初始容量、指定的负载因子创建一个HashMap。

  当创建一个HashMap时,系统会自动创建一个table数组来保存HashMap中的Entry,下面是HashMap中一个构造器的代码:

   Java代码

   //以指定初始化容量、负载因子创建HashMap

   publicHashMap(intinitialCapacity,floatloadFactor)

   {

   //初始容量不能为负数

   if(initialCapacity<0)

   thrownewIllegalArgumentException(

   "Illegalinitialcapacity:"+

   initialCapacity);

   //如果初始容量大于最大容量,让出示容量

   if(initialCapacity>MAXIMUM_CAPACITY)

   initialCapacity=MAXIMUM_CAPACITY;

   //负载因子必须大于0的数值

   if(loadFactor<=0||Float.isNaN(loadFactor))

   thrownewIllegalArgumentException(

   loadFactor);

   //计算出大于initialCapacity的最小的2的n次方值。

   intcapacity=1;

   while(capacity   capacity《=1;

   this.loadFactor=loadFactor;

   //设置容量极限等于容量*负载因子

   threshold=(int)(capacity*loadFactor);

   //初始化table数组

   table=newEntry[capacity];//①

   init();

   }

  上面代码中粗体字代码包含了一个简洁的代码实现:找出大于initialCapacity的、最小的2的n次方值,并将其作为HashMap的实际容量(由capacity变量保存)。例如给定initialCapacity为10,那么该HashMap的实际容量就是16。

  程序①号代码处可以看到:table的实质就是一个数组,一个长度为capacity的数组。

  对于HashMap及其子类而言,它们采用Hash算法来决定集合中元素的存储位置。当系统开始初始化HashMap时,系统会创建一个长度为capacity的Entry数组,这个数组里可以存储元素的位置被称为“桶(bucket)”,每个bucket都有其指定索引,系统可以根据其索引快速访问该bucket里存储的元素。

  无论何时,HashMap的每个“桶”只存储一个元素(也就是一个Entry),由于Entry对象可以包含一个引用变量(就是Entry构造器的的最后一个参数)用于指向下一个Entry,因此可能出现的情况是:HashMap的bucket中只有一个Entry,但这个Entry指向另一个Entry——这就形成了一个Entry链。

  

   HashMap的读取实现

  当HashMap的每个bucket里存储的Entry只是单个Entry——也就是没有通过指针产生Entry链时,此时的HashMap具有最好的性能:当程序通过key取出对应value时,系统只要先计算出该key的hashCode()返回值,在根据该hashCode返回值找出该key在table数组中的索引,然后取出该索引处的Entry,最后返回该key对应的value即可。看HashMap类的get(Kkey)方法代码:

   Java代码

   publicVget(Objectkey)

   {

   //如果key是null,调用getForNullKey取出对应的value

   if(key==null)

   returngetForNullKey();

   //根据该key的hashCode值计算它的hash码

   inthash=hash(key.hashCode());

   //直接取出table数组中指定索引处的值,

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

   e!=null;

   //搜索该Entry链的下一个Entr

   e=e.next)//①

   {

   Objectk;

   //如果该Entry的key与被搜索key相同

   if(e.hash==hash&&((k=e.key)==key

   ||key.equals(k)))

   returne.value;

   }

   returnnull;

   }

  从上面代码中可以看出,如果HashMap的每个bucket里只有一个Entry时,HashMap可以根据索引、快速地取出该bucket里的Entry;在发生“Hash冲突”的情况下,单个bucket里存储的不是一个Entry,而是一个Entry链,系统只能必须按顺序遍历每个Entry,直到找到想搜索的Entry为止——如果恰好要搜索的Entry位于该Entry链的最末端(该Entry是最早放入该bucket中),那系统必须循环到最后才能找到该元素。

  归纳起来简单地说,HashMap在底层将key-value当成一个整体进行处理,这个整体就是一个Entry对象。HashMap底层采用一个Entry[]数组来保存所有的key-value对,当需要存储一个Entry对象时,会根据Hash算法来决定其存储位置;当需要取出一个Entry时,也会根据Hash算法找到其存储位置,直接取出该Entry。由此可见:HashMap之所以能快速存、取它所包含的Entry,完全类似于现实生活中母亲从小教我们的:不同的东西要放在不同的位置,需要时才能快速找到它。

  当创建HashMap时,有一个默认的负载因子(loadfactor),其默认值为0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少Hash表(就是那个Entry数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap的get()与put()方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加Hash表所占用的内存空间。

  掌握了上面知识之后,我们可以在创建HashMap时根据实际需要适当地调整loadfactor的值;如果程序比较关心空间开销、内存比较紧张,可以适当地增加负载因子;如果程序比较关心时间开销,内存比较宽裕则可以适当的减少负载因子。通常情况下,程序员无需改变负载因子的值。

  如果开始就知道HashMap会保存多个key-value对,可以在创建时就使用较大的初始化容量,如果HashMap中Entry的数量一直不会超过极限容量(capacity*loadfactor),HashMap就无需调用resize()方法重新分配table数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为capacity的Entry数组),因此创建HashMap时初始化容量设置也需要小心对待。

上一页12下一页

视频学习

我考网版权与免责声明

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

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

最近更新

社区交流

考试问答