HashTable
继承关系
extends Dictionary<K,V> JDK较早提供的实现类
默认值
数组初始化大小:11
加载因子:0.75
基本属性
构造函数
增长方式
2*table.length+1
CRUD
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;
//key不能为null,key为null也会抛出空指针问题
//通过key的hash找到存储位置
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;
}
protected void rehash() {
int oldCapacity = table.length;
Entry<K,V>[] oldMap = table;
// overflow-conscious code
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;
}
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;
}
}
}
HashTable的put过程
1、判断value不能为null,若为null抛出异常-》hashtable中value不能为null
2、通过key进行hash获取到key该存储的索引位置
3、该索引位置的链表进行遍历,获取key是否存在(key存在条件 hash相等且通过key.equals判断相等)
4、在存在该key的情况下,将value值进行更新且直接返回
5、key不存在则进行新节点插入逻辑
5.1、扩容考虑:entry节点个数大于阈值 (count>threshold)进行扩容
5.2、新容量大小为:2*table.length+1
5.3、将原哈希表中的数据全部进行重新hash到新的hash表中
5.4、更新插入的key的新的位置
5.5、找到新节点位置,创建entry实体通过头插入将元素插入
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;
}
特点:get方法本身也具有线程安全性
get过程:
1、通过key来hash获取到存储索引位置-》通过key为null进行get操作也会抛出异常
2、遍历当前索引位置节点,判断是否相等(hash、equals),找到则直接返回value值
3、未找到则返回null
remove()删除操作
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index], 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;
}
删除操作本身也是线程安全的
删除过程:
1、通过key获取到当前存储的索引位置
2、对该索引位置链表进行遍历,找到key锁对应的entry实体、并进行删除
***HashMap和HashTable的异同点
相同点
不同点
==》对比学习
特点:
底层数据结构:数组+链表
null值:key、value不能为null
key重复性:key不能重复
有序性:不能保证插入有序
HashMap和HashTable的异同点
相同点
1、底层数据结构都为数组+链表
2、key都不能重复
3、插入元素有不能保证插入有序
4、哈希过程通过key进行hash
不同点:
1、安全性问题:
HashMap不能保证线程安全
HashTable能保证线程
2、继承关系:
HashMap继承自AbstractMap
HashTable继承自Dictionary
3、null值问题
HashMap的key和value都可以为null
HashTable的key和value都不能为null
4、扩容方式
HashMap按照2table.length
HashTable按照2table.length+1
5、默认值
HashMap默认数组大小为16
HashTable默认数组大小为11
6、hash算法不同
7、效率不同
HashMap在单线程小效率高
HashTable在单线程小效率低