HashMap和HashTable区别及Hash冲突解决方法

一、HashMap和HashTable主要有以下5个方面的区别:

1.继承的父类不同

  Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。

2.对null对象的支持不同

  HashMap是支持null键和null值的,而HashTable在遇到null时,会抛出NullPointerException异常。这并不是因为HashTable有什么特殊的实现层面的原因导致不能支持null键和null值,这仅仅是因为HashMap在实现时对null做了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。

3.容量大小及扩容方式不同

  HashMap和HashTable都使用哈希表来存储键值对。在数据结构上是基本相同的,都创建了一个继承自Map.Entry的私有的内部类Entry,每一个Entry对象表示存储在哈希表中的一个键值对。
Entry对象唯一表示一个键值对,有四个属性:
 -K key 键对象
 -V value 值对象
 -int hash 键对象的hash值
 -Entry entry 指向链表中下一个Entry对象,可为null,表示当前Entry对象在链表尾部。
  HashMap/HashTable内部用Entry数组实现哈希表,而对于映射到同一个哈希桶(数组的同一个位置)的键值对,使用Entry链表来存储。
  HashMap/HashTable初始容量大小和每次扩充容量大小的不同:HashTable默认的初始大小为11,之后每次扩充为原来的2n+1。HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。如果在创建时给定了初始化大小,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。

4.线程安全性不同

  HashTable是同步的(原因:公开的方法比如get都使用了synchronized描述符。而遍历视图比如keySet都使用了Collections.synchronizedXXX进行了同步包装),HashMap不是,也就是说HashTable在多线程使用的情况下,不需要做额外的同步,而HashMap则不行。
  由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap速度要慢。
  如果要保持线程安全可以选用ConcurrentHashMap,ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。Hashtable则会锁定整个map,Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map,ConcurrentHashMap比Hashtable高效。

5.Hash值不同

  Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号位改变,而后面的位都不变。

二、Hash冲突解决方法

  哈希(Hash)冲突:当关键字集合很大时,关键字值不同的元素可能会映像到哈希表的同一地址上,即K1!=K2,但f(K1)=f(K2),这种现象称为hash冲突。散列表要解决的一个问题就是散列值的冲突问题,通常有4种解决冲突的方法:
  (1)、链表法(链地址法):将相同hash值的对象组织成一个链表放在hash值对应的槽位,其中一个对象会记录下一个对象的地址;
  (2)、开放地址法:通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位;
  (3)、再Hash法:同时构造多个不同的哈希函数,当一个出现冲突时用另一个,直至没有冲突发生。这种方法不易产生聚集,但增加了计算时间。
  (4)、建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
  HashMap和Hashtable Hash冲突解决的方法采用的都是链表法,链表是单向链表。

总结:

       1、Hashmap总体比Hashtable高效,建议使用Hashmap,考虑线程安全的话,可以使用ConcurrentHashMap;
       2、JDK8 中哈希冲突过多,链表会转红黑树,时间复杂度是O(logn),不会是O(n),提高效率;
       3、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的。

猜你喜欢

转载自blog.csdn.net/alexlee1986/article/details/81080449