Java HashMap 与 Hashtable的区别
1、HashMap简介
HashMap是在JDK1.2引入的Map的实现类
HashMap核心是散列表(Hash table,也叫哈希表)
散列表是根据关键码值(Key value)而直接进行访问的数据结构
整体框架:HashMap是基于哈希表实现的,底层是基于 数组+链表 组成的,每个元素是一个键值对,其内部通过单链表(拉链法)解决冲突问题,容量不足(超过了阈值)时,同样会自动增长。
<默认容量是16,负载因子是0.75,在使用过程中不断地往里面存放数据,当数量达到了16*0.75=12时,就需要将当前16的容量进行扩容,而扩容的过程涉及到再哈希(rehash)、复制数据等操作,所以非常消耗性能。
因此通常建议能提前预估HashMap的大小最好,尽量减少扩容带来的性能损耗。
HashMap中真正存放数据的Entry<K,V>[] table,Entry是HashMap的一个内部类;
Entry的成员变量有:(1)key:写入的键;(2)value:写入的值;(3)next:解决hash冲入的引用,其指向下一个键值对;(4)hash:当前key的hashcode;>
是否线程安全:HashMap是非线程安全的,适用于单线程环境下,多线程环境下可以采用ConcurrentHashMap
能够被序列化与克隆?HashMap实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
HashMap存储数据的过程
HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单项链表。当准备添加一个键值对时,首先通过hash(key)方法计算hash值(return (key==null)?0:(h= key.hashCode())^(h>>>16);),然后通过indexFor(hash,length)求该键值对的存储位置,计算方法是先用hash&0x7FFFFFFF再对length取模,这样就保证每一个键值对都能够存入到HashMap中,当计算出的位置已经有数据时,由于存入位置是一个链表,则把这个键值对插入到链表头。
是否允许存储null值?
HashMap中key和value都允许为null。key为null的键值对永远都放在table[0]为头节点的链表中。HashMap只能有一个key为null值的键值,当多次存储key为null的键值对时,只会保存最近存储的,之前的都被覆盖掉了。HashMap可以有多个value为null的键值对。
为什么默认加载因子是0.75?
在理想情况下,使用随机哈希码,所有桶中节点的分布频率遵循泊松分布。在加载因子为0.75的情况下,遵循着参数平均为0.5的泊松分布。一个桶中链表长度达到8的概率为0.00000006,几乎是不可能事件。
2、HashTable简介
HashTable同样是基于哈希表实现的,每个元素是一个键值对,其内部也是通过单链表解决哈希冲突,容量不足时,同样会自动增长。
HashTable是JDK 1.0引入的类,是线程安全的,能用于多线程环境中。
HashTable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
3、二者不同
1、继承的父类不同
HashMap继承自AbstractMap类;HashTable继承自Dictionary类,但二者都实现了Map接口。Dictionary类是一个已经被废弃的类。
2、是否线程安全
HashMap线程不安全,HashTable线程安全,HashTable中的方法大多是synchronized的,而HashMap中的方法一般情况下是非synchronized的。在对多线程环境下,可以直接使用HashTable,不需要自己为它的方法实现同步,但使用HashMap时就必须自己增加同步处理。
HashTable实现线程安全的代价就是效率变低,因为会锁住整个HashTable,而ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定,效率比HashTable高很多。
3、包含的contains方法不同
HashMap是没有contains方法的,而包含containsValue和containsKey方法;
HashTable则保留了contains方法,效果同containsValue方法,还包含containsValue和containsKey方法
4、是否允许null值
HashMap是允许key和value为null值的,用containsValue和containsKey方法判断是否包含对应键值对;HashTable键值对都不能为空,否则报空指针异常。
5、计算hash值方式不同
为了得到元素的位置,首先需要根据元素的key计算出一个hash值,然后再用这个hash值来计算得到最终的位置。
HashMap:先判断key是否为null,若是,则hash值为0;若不是,则哈希值为(h= key.hashCode())^(h>>>16))
HashTable:计算key的hahsCode()方法得到的hash值就是最终的hash值
6、计算索引的方法不同
HashMap在求hash值对应的位置索引时,index=hash&(n-1)。将哈希表的大小固定为2的幂,因为是取模得到的索引值,这样取模时,直接做位运算,位运算比除法的效率要高很多;
HashTable在求hash值对应的位置索引时,做取模运算,int index=(hash&0x7FFFFFF)%tab.length;
<&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号位改变,而后面的位都不变>。
7、扩容方式不同
HashMap扩容必须要求为原容量的2倍,而且一定是2的幂次倍扩容结果,而且每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入;
而Hashtable扩容为原容量2倍加1;
8、解决hash冲突方式不同
jdk 8及以后,HashMap中,当出现哈希冲突时:
- 1、如果链表长度小于8时,则是以链表方式和解决冲突;
- 2、当链表长度大于等于8时,就会将链表装化成红黑树;
- 3、当红黑树元素小于等于6时,则优惠转化成链表存储。
在HashTable中, 都是以链表方式解决哈希冲突