简介之HashMap和Hashtable的区别
HashMap和Hashtable的区别
1.底层结构不同
1.1继承体系的区别
<ignore_js_op>
1.2内部实现使用的数组初始化和扩容方式不同
Hashtable:
(1) Hashtable继承于Dictionary类,实现了Map接口。Map是"key-value键值对"接口,Dictionary是声明了操作"键值对"函数接口的抽象类。
(2) Hashtable是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount。
table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。
count是Hashtable的大小,它是Hashtable保存的键值对的数量。
threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。
loadFactor就是加载因子。
modCount是用来实现fail-fast机制的
HashMap:
(1) HashMap内存储数据的Entry数组默认是16,如果没有对Entry扩容机制的话,当存储的数据一多,Entry内部的链表会很长,这就失去了HashMap的存储意义了。所以HasnMap内部有自己的扩容机制。HashMap内部有:
变量size,它记录HashMap的底层数组中已用槽的数量;
变量threshold,它是HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)
变量DEFAULT_LOAD_FACTOR = 0.75f,默认加载因子为0.75
HashMap扩容的条件是:当size大于threshold时,对HashMap进行扩容
2. 线程安全性的不同
(1)Hashtable是线程安全的,HashMap是线程非安全的。Hashtable的方法是synchronized的,而HashMap不是。
HashMap是Hashtable的轻量级实现(非线程安全的实现)。
synchronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。
(2)HashMap运行效率高:线程非安全,计算hash值的方式更快,扩容直接*2(位运算<<1)
Hashtable运行效率低:线程安全,计算hash值速度慢,扩容方式需要的时间相对长。
(3)HashMap就是为了提高运行速度设计的,相对于Hashtable而言,其计算hash值更容易产生“碰撞”现象,HashMap产生碰撞就以链表结构存储。HashMap的默认空间较大,时间换空间,都是在提高效率。
(4)在单线程环境时HashMap效率更高,在多线程中Hashtable能避免死锁,但是JDK1.5新增了concurrent包,引入线程安全的ConcurrentHashMap,使得Map也可以线程安全。它引入了一个“分段锁”的概念,不是所有方法都是同步的,因此效率相比Hashtable更高。所以尽管HashMap和Hashtable非常相似,但现在Hashtable已经很少用了(何况它还不符合驼峰命名规则)。
3. 对null的存储支持不同
1 public synchronized V put(K key, V value) {
2 // Make sure the value is not null
3 if (value == null) {
4 throw new NullPointerException();
5 }
6
7 // Makes sure the key is not already in the hashtable.
8 Entry<?,?> tab[] = table;
9 int hash = key.hashCode();
10 int index = (hash & 0x7FFFFFFF) % tab.length;
11 @SuppressWarnings("unchecked")
12 Entry<K,V> entry = (Entry<K,V>)tab[index];
13 for(; entry != null ; entry = entry.next) {
14 if ((entry.hash == hash) && entry.key.equals(key)) {
15 V old = entry.value;
16 entry.value = value;
17 return old;
18 }
19 }
20
21 addEntry(hash, key, value, index);
22 return null;
23 }
4.遍历方式的内部实现上不同
(1)Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
(2)HashMap的Iterator是fail-fast迭代器。当有其它线程改变了HashMap的结构(增加,删除,修改元素),将会抛出ConcurrentModificationException。不过,通过Iterator的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。
(3)JDK8之前的版本中,Hashtable是没有fast-fail机制的。在JDK8及以后的版本中 ,HashTable也是使用fast-fail的
5.计算hash值的方法不同
为了得到元素的位置,首先需要根据元素的KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。
Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。
Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模运算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高
为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。
6.小结
ps:详细原理大家可以百度更详细的底层实现。
更多免费技术资料可关注:annalin1203