https://blog.csdn.net/fxkcsdn/article/details/81487933
HashMap 和 Hashtable 区别
hash 数组初始化时机不同,Hashtable 是在构造函数初始化,而 HashMap 是在第一次 put()初始化 hash 数组。
在 HashTable 中,hash 数组默认大小是 11,增加的方式是 old*2+1。在 HashMap 中,hash 数组默认大小是 16,增加的方式是 2*old 而且一定是 2 的整数.
HashMap 允许空(null)键值(key),而 HashTable 不允许。
HashMap 把 Hashtable 的 contains()方法去掉了,改成了 containsvalue()和 containsKey()。
Hashtable 的方法是线程安全的,而 HashMap 不支持线程的同步,不是线程安全的。
Hashtable 使用 Enumeration,HashMap 使用 Iterator。
hash 值的使用不同,HashTable 直接使用对象的 hashCode。
下面就通过源码来证明上面的 7 个不同。
1.hash 数组初始化时机不同
//HashTable 构造器
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException(“Illegal Capacity: “+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException(“Illegal Load: “+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];//初始化 Hash 数组
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
initHashSeedAsNeeded(initialCapacity);
}
//hashMap 的 put 函数
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);//初始化 Hash 数组
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
2.在 HashTable 中,hash 数组默认大小是 11,增加的方式是 old*2+1。在 HashMap 中,hash 数组默认大小是 16,增加的方式是 2*old 而且一定是 2 的整数.
//Hashtable 构造器
public Hashtable() {
this(11, 0.75f);//默认大小 11
}
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;//增加方式是 2*old+1
//省略。。。。。。
}
//HashMap 源码
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认大小 16
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);//扩容方式是:2*old
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
3.HashMap 允许空(null)键值(key),而 HashTable 不允许。
这里发现 Hashtable 只对 value 为空进行了判断,却没有对 key 是否为空判断,然后就用 hash(key)计算 hash 值,那么如果传入的键值为空的话,程序会报错。
//Hashtable 源码
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();//如果值为空就抛出异常
}
Entry tab[] = table;
int hash = hash(key);
//省略。。。
}
private int hash(Object k) {
// hashSeed will be zero if alternative hashing is disabled.
return hashSeed ^ k.hashCode();
}
//HashMap 源码
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
//省略。。。。。。
}
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
这里可以看出,hashmap 是支持插入 key 为 null 的键值对的,而且保存在 hash 数组 index=0 位置。
4.HashMap 把 Hashtable 的 contains()方法去掉了,改成了 containsvalue()和 containsKey()。
hashtable 的 contains()方法就是判断 hash 数组中是否有对应的 value 值,所以 contains()方法名字起的不好,HashMap 舍弃之,并重新命名为 containsvalue(),清晰明了。
//hashtable 源码
public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}
Entry tab[] = table;
for (int i = tab.length ; i– > 0 {
for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
//hashMap 源码
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
5.Hashtable 的方法是线程安全的,而 HashMap 不支持线程的同步,不是线程安全的。
参考 4 中代码,可以看出 Hashtable 是加了 synchronized 关键字,而 hashmap 放弃同步来提高性能。
6.Hashtable 使用 Enumeration,HashMap 使用 Iterator。
区别这里就不说了,大家可以百度。
7.hash 值的使用不同,HashTable 直接使用对象的 hashCode。
//hashtable 的 hash 函数
private int hash(Object k) {
// hashSeed will be zero if alternative hashing is disabled.
return hashSeed ^ k.hashCode();
}
//hashmap 的 hash 函数
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
hashmap 的 hash 函数多次进行异或右移操作,目的是将 hash 值的高位和地位都参与运算,避免桶位聚集现象,让数据分散的更均匀一点。