介绍
- 和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射,底层实现由“数组+链表”实现,相对于hashMap来说简单很多。
- Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
- Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。
- Hashtable中的映射不是有序的
- 不建议使用,以后说不定哪天就废掉了。连官方文档也说了,如果在非线程安全的情况下使用,建议使用HashMap替换,如果在线程安全的情况下使用,建议使用ConcurrentHashMap替换。
属性
public class Hashtable<K,V> extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
// Hashtable保存数据的数组
private transient Entry<?,?>[] table;
// hashtable的容量
private transient int count;
// 阈值
private int threshold;
// 负载因子
private float loadFactor;
// 结构性修改
private transient int modCount = 0;
}构造函数
//指定初始容量和加载因子构造函数
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];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
/**
指定初始容量和默认加载因子(0.75)构造函数
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
默认构造函数,初始容量11,加载因子0.75
*/
public Hashtable() {
this(11, 0.75f);
}
/**
给定的map具有相同映射关系的新的hash表
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}添加操作
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();//获取key的hashCode
int index = (hash & 0x7FFFFFFF) % tab.length;//确认key的索引位置
//根据索引找到所处的位置
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//循环查找,寻找key,并替换
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
Entry<?,?> tab[] = table;
//判断是否需要扩容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
//将元素插入到对应的链表第一个位子上 直接加 不需要判断是否存在 调用的地方判断
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
modCount++;
}
//扩容方法
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//扩容大小是原来的2倍+1
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 = (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;
}
}
}putAll方法和put方法类似,不再做具体介绍
public synchronized void putAll(Map<? extends K, ? extends V> t) {
//依次将数据,添加到映射关系中
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
}删除操作
//删除置顶key的元素,相对于hashMap简单很多
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
//循环找到对应的元素,并删除
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
modCount++;
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}查找操作
//很简单,不在介绍
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}总结
- 首先,Hashtable底层是通过数组加链表实现的,这点和JDK1.8之前的HashMap差不多。
- 其次,Hashtable是不允许key或者value为null的。
- 并且,Hashtable的计算索引方法,默认容量大小,扩容方法都与HashMap不太一样。
- 其实我们可以看到,Hashtable之所以线程安全,大部分方法都是使用了synchronized关键字,虽然JDK优化了synchronized,但在方法上使用该关键字,无疑仍旧是效率低下的操作。就这方面来说,ConcurrentHashMap无疑比Hashtable好多了,后续会有专门文章介绍ConcurrentHashMap,这里就不多说了。
总之呢,Hashtable无疑算是废掉了,说不定过不了多久,它就消失在Map框架中了呢。
版权声明:本文为qq_39736103原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。