两者的父类不同
HashMap是继承自AbstractMap类,而HashTable 是继承自Dictionary类.不过他们都实现了Map Cloneable Serializable这三个接口.
对null的支持不同
HashTable:key和value都不能为null
HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null
安全性不同
HashMap是线程不安全的,在多线程并发的环境下,可能产生死锁等问题,因此需要开发人员自己处理多线程问题.
HashTable是线程安全的,它的每个方法上都有synchronized关键字,因此可以直接用于多线程中.
虽然HashMap不安全,但是它的效率远远高于HashTable,这样设计是合理的,因为大部分的使用场景都是单线程的.当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap.
ConcurrentHashMap虽然也是线程安全的,但是它的效率比HashTable要高好多倍.因为ConcurrentHashMap使用了分段锁,并不多整个数据进行锁定
初始容量大小和每次扩充容量大小不同
HashMap初始化默认的数组大小只有16,自动扩容是翻一倍
HashTable 初始化默认是 11 (除质数球余的分散效果好),自动扩容是两倍+1(奇数)
计算hash值的方法不同
- HashMap
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
上面可以看出key可以为null,key值的hash值的计算方法为:** key的hashCode的高16位不变,低16位与高16位异或的结果作为低16位的结果.(h>>>16,表示无符号右移16位,高位补0)**
- HashTable
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length
直接使用key的hashCode,但是为了在hashCode为负数的情况下,取出负数,所以和0x7FFFFFFF (二进制为0111 1111 1111 1111 1111 1111 1111 1111) 负数和其& 操作将产生一个正数.
扩展
分段锁
容器里面有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里面不同数据段的数据时,线程间就不会存在锁竞争,从而有效提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据的时候,其他段的数据也能被其他线程访问.