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值更加平均的分散在hash数组的bucket桶中。