;HashMap
跟HashTable
的区别大家都知道,都继承了Map
接口,可以存储<Key , Value>
对,然后一个是线程安全的,另一个不是线程安全的。同样,现在即使是在多线程环境下也不推荐使用HashTable
,推荐使用java.util.concurrent
包中的ConcurrentHashMap
,由于使用了分段锁,会得到更好的性能。
实现框架
HashTable
的基本原理比较简单,在类中实现了Map
接口和Map接口中的Entry
接口。Entry
接口的实现是一个链表,类的声明及成员变量如下:
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
final K key;
V value;
Entry<K,V> next;
}
在HashTable
中比较重要的成员变量有
/**
* 存储数据的Entry数组
*/
private transient Entry<K,V>[] table;
/**
* 在hash table的entry数量
*/
private transient int count;
/**
* table重新hash的阈值,计算方法为(int)(capacity * loadFactor).)
*/
private int threshold;
/**
* 上面公式用到的loadFactor
*/
private float loadFactor;
HashTable
的工作原理就是对一个传递进来的参数key
求其hash值,对table的长度取余,得到index
值,然后将对应key的value存到Entry
中,如果在table[index]
中已经有entry了,则会将new
的Entry
用头插法插入到链表中。
// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
Entry
的构造函数:
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
发生rehash的情况
先把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 = hash(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)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
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;
}
- 可以看到当重复put一个key(hash相同,且equal)时,会将old值替换,并且返回old值,否则返回null。
- 不允许null的value出现
- 当count > threshold 时,会增大table的长度,并且重新hash。
rehash()的具体逻辑
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1;
//about twice
if (newCapacity - MAX_ARRAY_SIZE > 0) { //判断newCapacity是否溢出
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;
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;
}
}
}
流程图为: