HashTable与HashMap

版权声明:本文为博主原创文章,未经博主允许不得转载。http://www.cnblogs.com/jokermo/

1.区别

  • HashTable是线程安全的,HashmMap是非线程安全的
  • HashTable不允许键和值为null,HashMap允许键和值为null
  • HashMap提供了可供应用迭代的键的集合,所以HashMap是快速失败的,HashTable提供对键的枚举,是安全失败
  • 扩容的参数不一样,HashMap要保证每次扩容后是2的次方,HashTable是扩大一倍
  • 散列计算不一样

虽然HashTable是线程安全的,适用于多线程。但是一般不推荐使用,因为HashTable是遗留类,内部实现很多没有优化和冗余,使用ConcurrentHashMap代替HashTable在多线程中使用。

2.HashMap底层实现

HashMap是存储键值对的聚合,也就是以Key-Value为键值对存储,也称为Entry。Entry分散存储在一个数组中。HashMap最常用的方法是Get与Put.

Put的原理

例如这是一个HashMapd的数组,初始化时都为null

现在调用HashMap.Put("dog","0");

这时候我们需要利用一个哈希函数来确定Entry的插入位置(index):

index =  Hash(“dog”)

假定最后计算出的index是2,那么结果如下:

 

但是HashMap数组的长度是有限的,如果hash()出来的index对应的值中已经有数据了,这样就发生了hash冲突比如index = Hash("cat")

扫描二维码关注公众号,回复: 114007 查看本文章

这时候就要用链表来解决了。HashMap数组的每一个元素不止是一个Entry对象,也是一个链表的头节点。每一个Entry对象通过Next指针指向它的下一个Entry节点。当新来的Entry映射到冲突的数组位置时,

只需要插入到对应的链表即可:

注意:这里使用的是“头插法”,因为设计者认为后插入的数据更容易被查找。

Get的原理

当调用Get("dog")时,首先

首先会把输入的Key做一次Hash映射,得到对应的index:index =  Hash(“apple”)

以为存在hash冲突,对应的index找到的值不止一个,所以这个列子会进行两步操作:

第一步:

先找到cat,cat不是我们想要的,然后进行往下找。

第二步:

找到dog,完成!

到现在为止HashMap的底层原理基本完成。接下来是更底层的东西了!!!

3.HashMap底层实现

HashMap的默认长度为什么是16?每次扩容为什么是2的次幂?

之所以选择16,是为了服务于从Key映射到index的hash算法。为了实现一个尽量均匀分布的Hash函数,设计者采用了位运算方式。公式如下:

index =  HashCode(Key) &  (Length - 1)(Length是HashMap的长度)

下面我们以值为“book”的Key来演示整个过程:

1.计算book的hashcode,结果为十进制的3029737,二进制的101110001110101110 1001。

2.假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制的1111。

3.把以上两个结果做与运算,101110001110101110 1001 & 1111 = 1001,十进制是9,所以 index=9。

可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。

长度16或者其他2的幂,Length-1的值是所有二进制位全为1,这种情况下,index的结果等同于HashCode后几位的值。只要输入的HashCode本身分布均匀,Hash算法的结果就是均匀的。

注:列子取自公众号《程序员小灰》

 

猜你喜欢

转载自www.cnblogs.com/jokermo/p/8995985.html