转自https://blog.csdn.net/duoduo18up/article/details/80167074
1.定义
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>,Cloneable,java.io.Serializable
继承自Dictionary。
- Dictionary是一个抽象父类,功能和Map一样,但过时了,官方推荐使用Map接口来替代。
实现了Map接口,以及Cloneable,Serializable接口。
2.和HashMap的区别
2.1 null值的问题
Hashtable的键(key)和值(value)均不能为null
/**
* 将key和value加入到map中,明显标明,
* value不能为null。如果key为null,则会报NullPointerException异常
* */
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the Hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//很直接的利用hashCode去除table.length,然后取长度。
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//链表后面有数据
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
//hash相同且equals,那么就连在后面,是用链表的方式。
V old = entry.value;
entry.value = value;
return old;
}
}
//第一个,链表后面没有数据。
addEntry(hash, key, value, index);
return null;
}
关于value,明显有if判断,不能为null。
如果key为null,则也直接在计算hashCode的时候就会报空指针异常。
2.2 计算table数组索引值的方法
int hash=key.hashCode();//直接用hashcode%n
int index=(hash&0x7FFFFFFF)%tab.length
而HashMap中:
int index=e.hash%n;
hash值的计算方法不同,很直接地利用hashCode去除table.length,然后取余数。
2.3 Hashtable是线程安全的
因为Hashtable中的大多数方法都是加了synchronized关键字,所以同一时刻只能有一个线程进入其方法,故是线程安全的。
2.4initialCapacity和loadFactor
HashMap中:initialCapacity=16,loadFactor=0.75;
Hashtable中:initialCapacity=11,loadFactor=0.75;
2.5解决冲突的方式
HashMap:链表/红黑树
- 冲突数量<8,以链表方式解决冲突
- 冲突数量>=8,将冲突的Entry转换为红黑树进行存储
- 又当冲突数量<6时,有转换为链表进行存储
Hashtable:只有链表
2.6 扩容的额度
HashMap中:一旦扩容,都是扩展到2的倍数。因为这样有利于计算数组索引值。即,和计算数组索引结合起来
Hashtable中:一次性扩展为oldCapacity*2+1
/**
* 一次扩展是,old*2+1
*/
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
//新的threshold值。取newCapacity*loadFactor的小值。
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
先扩展,再把就数组里面的元素一个一个添加到新的里面。
注意:这里取hash而不是e.hash,而仍然是key.hashCode计算保留下来的值。