0. HashTable是什么
- 继承
Dictionary
-
put
get
remove
等方法是synchronized
修饰 - 存储
类型
1. 主要数据结构
使用Entry
数组存储数据
使用链表解决哈希冲突
/**
* The hash table data.
*/
private transient Entry,?>[] table;
/**
* Hashtable bucket collision list entry
*/
private static class Entry implements Map.Entry {
final int hash;
final K key;
V value;
Entry next;
//省略部分代码
....
}
2. 主要api解析
2.1 构造函数
默认的初始容量是11,装载因子是0.75
如果参数是Map
初始容量是Max(原容量的2倍, 11)
public Hashtable()
public Hashtable(int initialCapacity)
public Hashtable(int initialCapacity, float loadFactor)
public Hashtable(Map extends K, ? extends V> t)
2.2 put方法
Neither the key nor the value can be
null
从方法注释中可以看到Key
Value
均不能为null
/**
* Maps the specified key
to the specified
* value
in this hashtable. Neither the key nor the
* value can be null
.
*
* The value can be retrieved by calling the get
method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or null
if it did not have one
* @exception NullPointerException if the key or value is
* null
* @see Object#equals(Object)
* @see #get(Object)
*/
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();
//求取元素的index
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry entry = (Entry)tab[index];
for(; entry != null ; entry = entry.next) {
//找到key相同的节点,说明以前存储过,直接更新value
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) {
modCount++;
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.
//容量未达到阈值,或者已经处理过,新建一个节点,插入当前index链的最前
@SuppressWarnings("unchecked")
Entry e = (Entry) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
其中关于threshold
部分,一般情况下不会有MAX_ARRAY_SIZE
这么大,所以还是容量*装载因子
居多。
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
2.3 get方法
直接使用index查找,使用equals
判断
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key.equals(k))},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or
* {@code null} if this map contains no mapping for the key
* @throws NullPointerException if the specified key is null
* @see #put(Object, Object)
*/
@SuppressWarnings("unchecked")
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;
}
2.4 remove方法
从链中移除对应的entry
/**
* Removes the key (and its corresponding value) from this
* hashtable. This method does nothing if the key is not in the hashtable.
*
* @param key the key that needs to be removed
* @return the value to which the key had been mapped in this hashtable,
* or null
if the key did not have a mapping
* @throws NullPointerException if the key is null
*/
public synchronized V remove(Object key) {
Entry,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry e = (Entry)tab[index];
for(Entry prev = null ; e != null ; prev = e, e = e.next) {
//找到对应的节点
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
//如果是中间的节点,pre链上next
if (prev != null) {
prev.next = e.next;
//如果是头节点,tab[index]链上next
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
2.5 扩容
- 扩容时容量扩大为
2*n+1
大小
/**
* Increases the capacity of and internally reorganizes this
* hashtable, in order to accommodate and access its entries more
* efficiently. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
*/
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry,?>[] oldMap = table;
// overflow-conscious code
//容量扩大为2*n+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 ;) {
//将旧的map中的数据逐一放到新的map中,旧的entry的index在新的map中可能会不同,因此会重新计算
for (Entry old = (Entry)oldMap[i] ; old != null ; ) {
Entry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
//插入到对应链表的最前
e.next = (Entry)newMap[index];
newMap[index] = e;
}
}
}
3. 与HashMap
的区别
-
HashTable
中-
Key
Value
都不能为null
- 方法是
synchronized
修饰的,即线程安全的 - 定位元素的
index
是取余的方式,因为长度不是2的幂 - 没有使用红黑树
- 插入节点时,
HashTable
是插入到链的最前,HashMap
是插入到链的最后 - 扩容时变为
2*n+1
,HashMap
扩大为原来的2倍
-
4. 参考
- HashTable源码build 1.8.0_121-b13版本