浅析JDK1.8 Hashtable源码。
写在开篇
简单介绍一下Hashtable:
- 和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射
- Hashtable是线程安全的,HashMap是非线程安全的
- Hashtable的key、value都不可以为null
类的继承关系
Hashtable继承于Dictionary,实现了Map(规定了Map的操作规范)、Cloneable(可拷贝)、java.io.Serializable(可序列化)这几个接口。1
2
3public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
存储结点–Entry
1 | private static class Entry<K,V> implements Map.Entry<K,V> { |
成员变量
1 | /** |
构造方法
1 | /** |
可以看到:
- HashMap对底层数组采取的懒加载,即当执行第一次插入时才会创建数组,而Hashtable在初始化时就创建了数组
- HashMap中数组的默认初始容量是16,并且必须的是2的指数倍数,而Hashtable中默认的初始容量是11,并且不要求必须是2的指数倍数。
Map接口的实现
put()
1 | public synchronized V put(K key, V value) { |
接着看下addEntry()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// 扩容
rehash();
tab = table; // 更新tab
hash = key.hashCode(); // 更新hash
index = (hash & 0x7FFFFFFF) % tab.length; // 更新下标
}
// 头插法插入新元素
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
再看看rehash():1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// 扩容操作
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
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 ;) {
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;
}
}
}
get()
1 | public synchronized V get(Object key) { |
remove()
remove()先查询键值相同的元素,如果存在则删除结点,同时modCount++,count–。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
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;
}