(一)底层数据结构
HashTable的数据结构和HashMap的数据结构相同,都是有散列表构成的,内部都是由Entry<K,V>来维护的。
(二)继承关系
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
继承Dictionary,实现了Map、Cloneable接口
(三)默认值
默认大小:11
默认加载因子:0.75
(四)基本属性
/**
* 哈希表
*/
private transient Entry<K,V>[] table;
/**
* 元素个数
*/
private transient int count;
/**(int)(capacity * loadFactor)
*扩容阈值
*/
private int threshold;
/**
*加载因子
*/
private float loadFactor;
(五)构造函数
(1)
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);
initHashSeedAsNeeded(initialCapacity);
}
(2)
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
(3)
public Hashtable() {
this(11, 0.75f);
}
(4)
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
(六)增长方式
(七)CURD(增删改查)
1、put()方法——添加键值对
(一)判断value不能为null,如果为null,抛出空指针异常;
(二)通过key的哈希,得到索引位置
(三)对该索引位置的链表进行遍历,key是否存在,(条件:if ((e.hash == hash) && e.key.equals(key)))
(四)在key存在的情况下,将key的value值进行更新,并返回原来的value值
(五)key不存在,创建新结点,进行插入;
(1)扩容考虑:count >= threshold
(2)新容量大小,(2倍加一)
(3)将原哈希表中所有的数据进行重新哈希,至新的哈希表中
(4)更新插入的key的新位置
(5)找到新结点位置,创建新结点,通过头插法将元素插入
public synchronized V put(K key, V value) {
// Make sure the value is not null
//value不能为null,抛出空指针异常
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
//key也不能为null
int hash = hash(key);//通过key的哈希
int index = (hash & 0x7FFFFFFF) % tab.length;//hash过程
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
//元素个数大于容量的0.75,扩容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}
rehash()方法——扩容方法
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;//2倍+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<K,V>[] newMap = new Entry[newCapacity];//扩容
modCount++;
//更新扩容阈值,取新容量*加载因子与最大容量加一中的最小值
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
boolean rehash = initHashSeedAsNeeded(newCapacity);
table = newMap;
//重新计算hash值
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
if (rehash) {
e.hash = hash(e.key);
}
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}
2、get()——获取元素
具有线程安全性
(1)通过key来哈希,得到key存储的索引位置
注:key为null,也会抛出空指针异常
(2)遍历当前索引位置结点,判断是否相等,找到则直接返回value,未找到返回null
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);//根据key哈希,得到相应的索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
//遍历当前索引位置结点
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {//哈希值相等,键值相等,返回value
return e.value;
}
}
return null;//没有找到返回null
}
3、remove()——删除操作(线程安全)
(1)通过key获取当前索引位置
(2)对该索引位置链表进行遍历,找到key所对应的entry实体,并进行删除,返回对应的value,未删除返回null
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = hash(key);//根据key哈希
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {//从开始找
if ((e.hash == hash) && e.key.equals(key)) {//哈希值相等,键值相等
modCount++;
if (prev != null) {//删除的不是头结点
prev.next = e.next;//直接将前驱指向后继
} else {//删除头结点
tab[index] = e.next;//直接将next作为当前索引位置链表的头结点
}
count--;//count减一
V oldValue = e.value;
e.value = null;//便于回收
return oldValue;//返回删除键的value
}
}
return null;
}
(八)特点
1.维护Hashtable内部的是一个Entry,默认容量为11,负载因子为0.75,
2.扩容方式当Entry已用空间>=总空间的75%,总空间扩大到原来的2倍。(因为每次扩容都要计算桶位,这样扩容可以减少扩容次数)
3.Hashtable所有的方法都有synchronized修饰线程安全
4. Hashtable不可以存Null值,
5.HashTable的容量指的是Entry<K,V>[] table的长度
6.计算桶位的方式int index= (hash & 0x7FFFFFFF) % tab.length