HashTable和HashMap一样,都是通过哈希表实现的,但不同的是,HashTable是线程安全的,在1.8中,它们不一样的地方有很多。
从put方法进入
public synchronized V put(K key, V value) {
// Make sure the value is not null
// value值不可以为空
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
//table即为已存在的数组或者初始化过得数组
Entry<?,?> tab[] = table;
//和HashMap不一样的是,这里的hash是hashCode的值
int hash = key.hashCode();
//与31个1与操作,对数组长度取余,防止数组下标越界
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//遍历,这里的功能是替换重复的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;
}
}
//没有重复的key值,增加节点
addEntry(hash, key, value, index);
return null;
}
查看增加节点的方法:
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
//是否超过阈值,默认阈值是8(就是11*0.75f)
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")
//将原来下标为index的元素赋值给e
Entry<K,V> e = (Entry<K,V>) tab[index];
//将新的节点放在下标为index的位置并将新节点的next设置为e,
//从这看出来排列的顺序和插入的顺序是相反的
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
扩容方法:
protected void rehash() {
int oldCapacity = table.length;
//旧的数组给oldMap
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;
}
// new出新容器,到这新容器还是空的
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 ; ) {
//旧的元素值给e
Entry<K,V> e = old;
//old指向原来元素的下一个元素
old = old.next;
//给新的元素新的下标,因为没有HashMap设计的精妙,这里的下标位置并没有什么规律
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
//将新下标位置的元素给e的next,e是原来的old,所以再下一行将e给新的下标位置
//实际上是将原来的结构反过来,下面给出图文
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
这是上面for循环的一些过程:
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
空间有限,凑合看