源码分析HashMap与HashHashTable的区别
这个题在面试中命中率极高的一个面试题,今天就和大家谈谈两者的区别有哪些。
1、继承父类不同
看源码可知:HashTable继承的父类是Dictionary(字典的意思),而HashMap继承的父类是AbstractMap类。但两者实现的接口都一样。
2、线程安全问题
HashTable是线程安全的,HashMap是线程不安全的。
HashTable类中的每个方法都给加了synchronized
。虽然HashMap不是线程安全的,但是它的效率会比HashTable要好很多。如果是单线程操作的话,HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
3、key为null的处理不同
/** HashTable源码 **/
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
HashTable的put()方法的注释中已经详细说明:当你的key或者value为null的话就会抛出空指针异常。
/** HashMap源码 **/
/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower. Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.) So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
HashMap的put()方法返回体里详细说明:当key为null,会把key值等于0进行hashcode进行计算。
4、初始容量大小不同
HashMap的初始容量是16,每一次创建一个map初始大小是往map里put值时才会开始初始容量大小。而HashTable的初始容量是11,每次new的时候就会初始这个容量。
5、扩充容量大小不同
HashMap扩充容量是原来容量的2倍,HashTable扩充容量是原来的2n+1倍。
Hashtable的侧重点是哈希结果更加均匀,使得哈希冲突减少。当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。而HashMap则更加关注hash的计算效率问题。在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。这从而导致了Hashtable和HashMap的计算hash值的方法不同。
6、计算hash值的方法也不同
/** HashTable源码 **/
Hashtable直接使用对象自身的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。hash & 0x7FFFFFFF 是将哈希值变为正数,然后再使用除留余数发来获得最终的位置并映射到哈希表中。Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。
/** HashMap源码 **/
‘>>>’是无符号右移操作,高位补0.
首先判断key是不是等于null,等于null,则返回0作为哈希值。否则,运算(h=key.hashCode()) ^ (h >>> 16),将key的哈希值的高位与低位异或的结果作为低位,改为不变。
首先判断key是不是等于null,等于null,则返回0作为哈希值。否则,运算(h=key.hashCode()) ^ (h >>> 16),将key的哈希值的高位与低位异或的结果作为低位,改为不变。