Java集合系列08之 Hashtable
参考:jdk 1.7 源码
第1部分 Hashtable介绍
- 和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。
- Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
- Hashtable 的函数都是同步的,这意味着它是线程安全的;
- Hashtable 的key、value都不可以为null。
- Hashtable 中的映射是无序的。
- 扩容:新容量 = 旧容量*2+1 ;且不能超过极限值。
- HashTable在实例化时即分配内存,与HashMap在进行第一个put时分配内存有区别。
- put操作:根据key值通过下图操作获取下标,存入主链数组下标所在位置,如果发生hash冲突,则有一个应为指向原先存储的Entry。
- get操作:根据key值通过下图操作获取下标,如果不存在冲突链,直接返回对应Entry,如果存在冲突链,遍历冲突链获取对应Entry
第2部分 Hashtable的整体结构
和HashMap一样,Hashtable由数组+链表组成的,数组是Hashtable的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
第3部分 Hashtable的核心属性
和HashMap 基本一样
// Hashtable主干
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//实际存储的key-value键值对的个数
transient int count;
//阈值,当table == {}时,该值为初始容量(初始容量默认为11);
//为table分配内存空间后,threshold一般为 capacity*loadFactory。Hashtable在进行扩容时需要参考threshold
int threshold;
//负载因子,代表了table的填充度有多少,默认是0.75
final float loadFactor;
//Hashtable结构修改的次数。
//用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,
//如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),
//需要抛出异常ConcurrentModificationException
transient int modCount
// Entry 内部类
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash; //hash值
final K key; //key
V value; //value
Entry<K,V> next; //冲突链引用(单向链表),没冲突就指向null
}
// volatile 关键字保证同步
private transient volatile Set<K> keySet = null;
private transient volatile Set<Map.Entry<K,V>> entrySet = null;
private transient volatile Collection<V> values = null;
第4部分 Hashtable源码解析
put 操作
public synchronized V put(K key, V value) {
// 保证 value 不为空
if (value == null) {
throw new NullPointerException();
}
// 判断 key 值是否已存在,存在则修改value,否则新增
Entry tab[] = table;
int hash = hash(key); // 获取hash值
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++;
// 判断容量,如果扩容,根据新HashTable重新计算hash值 和 下标
if (count >= threshold) {
// 扩容
rehash();
tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}
// 新增节点
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}
// hash 计算参数 , 保证散列均匀
transient int hashSeed;
// 初始化 hashSeed
final boolean initHashSeedAsNeeded(int capacity) {
boolean currentAltHashing = hashSeed != 0;
boolean useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
boolean switching = currentAltHashing ^ useAltHashing;
if (switching) {
hashSeed = useAltHashing
? sun.misc.Hashing.randomHashSeed(this): 0;
}
return switching;
}
// hash 函数
private int hash(Object k) {
// 如果禁用散列,哈希种子将为零。
return hashSeed ^ k.hashCode();
}
// 扩容
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// 扩容:新容量 = 旧容量*2+1
int newCapacity = (oldCapacity << 1) + 1;
// 保证扩容不能超过极限值
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
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;
// 迭代所有数据,重新进行hash计算,存入新Hashtable中
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;
}
}
}
get 操作
public synchronized V get(Object key) {
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)) {
return e.value;
}
}
return null;
}
第5部分 Hashtable遍历方式
- 遍历Hashtable的键值对
Iterator iter = table.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
Object key = entry.getKey();
Object value= entry.getValue();
}
- 通过Iterator遍历Hashtable的键
Iterator iter = table.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object value= table.get(key);
}
- 通过Iterator遍历Hashtable的值
Collection c = table.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
Object value = iter.next();
}
- 通过Enumeration遍历Hashtable的键
Enumeration enu = table.keys();
while(enu.hasMoreElements()) {
System.out.println(enu.nextElement());
}
- 通过Enumeration遍历Hashtable的值
Enumeration enu = table.elements();
while(enu.hasMoreElements()) {
System.out.println(enu.nextElement());
}