结构
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
}
Hashtable继承自Dictionary,实现了Map<K,V>, Cloneable, java.io.Serializable接口。
内部类
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
//省略方法
}
Hashtable存储节点的内部类,实际是一个单链表。
属性
//用于存放元素的数组
private transient Entry<?,?>[] table;
//table中的元素个数
private transient int count;
// 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)
private int threshold;
//加载因子
private float loadFactor;
//修改次数,用于fail-fast机制
private transient int modCount = 0;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = 1421746759512286392L;
构造函数
初始容量和加载因子参数
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;//初试容量为0,则修改为1
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];//table的长度为initialCapacity
//阈值为initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1中的最小值。
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
只传入initialCapacity参数
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);//默认加载因子为0.75
}
默认构造函数
public Hashtable() {
this(11, 0.75f);//初试容量为11,加载因子为0.75
}
传入Map
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);//初始容量为2倍Map的大小和11中的最大值,加载因子为0.75
putAll(t);
}
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());
}
方法:
contains
public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}
Entry<?,?> tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
遍历数组,返回寻找value相等的。
containsKey
public synchronized boolean containsKey(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 true;
}
}
return false;
}
先用key算出hash值,再用 (hash & 0x7FFFFFFF) % tab.length 算出数组中的索引值。
get方法:
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//通过key的hash值和table的length,经过运算得到散列表中的索引
int index = (hash & 0x7FFFFFFF) % tab.length;
//遍历tab[index]对应的链表
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
int hash = key.hashCode();
//通过key的hash值和table的length,经过运算得到散列表中的索引
int index = (hash & 0x7FFFFFFF) % tab.length;
算出索引的步骤很重要。
put方法:
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();
//计算索引
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//如果索引位置有元素了,且hashcode和key都相等,直接覆盖value值。
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;
}
addEntry
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// 如果count>阈值,进行扩容。
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++;
}
remove
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)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}
计算出索引值,判断是否有hash和key都相等的,如果有就移去。
扩容方法
rehash
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;//新容量=旧容量*2+1
if (newCapacity - MAX_ARRAY_SIZE > 0) {//新容量>2^31-9
if (oldCapacity == MAX_ARRAY_SIZE)//旧容量 = 2^31-9,直接返回
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;新容量=2^31-9
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
//对散列表内的所有元素进行“重排列”,顺序从后往前
for (int i = oldCapacity ; i-- > 0 ;) {
//遍历oldMap[i]对应的entry链表
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;
/**
* 可能e.next = (Entry<K,V>)newMap[index];和newMap[index] = e;比较生硬,我们举例说明
* 第一次遍历时,newMap[index]为null,所以 e.next=null
* 第二次遍历时,假定e1=e.next, 然后 e1.next = e, newMap[index] = e1(起始点为e1)
* ...
* 依次进行,会发现链表中插入元素的顺序是“从左往右”
*/
}
参考https://blog.csdn.net/qq_29864971/article/details/79326106