上次咱们说到jdk1.8的HashMap原理,这次分析一下HashTable
1.HashMap与HashTable的区别是面试官经常问的一个问题,对于两者的区别有以下几点
- HashMap允许null值,而HashTable不允许null值
- HashMap是非线程安全的,而HashTable是线程安全的,线程安全会影响读取效率,故HashMap的效率高些
- 初始化大小不相同HashMap初始化大小为16,HashTable初始化大小为11,两者的默认加载因子为0.75
2.结构
HashTable与HashMap的结构是一样的,在这不做说明,需要的同学点击下方链接 HashMap
HashTable最主要的为三个方法 put(),get(),rehash();
put()方法主要逻辑如下
- 判断key是否为空,为空丢异常
- 如key不为空,则对key进行散列值,也就是调用hashCode方法。
- 经过对key的hash运算得出数组下标
- 根据下标对获取Entry,如为空,直接添加,如不为空,则代表当前位置已有元素,会进行判断当前key的hash与位置已有元素key的hash值是否相同,如果不相同,走添加方法,如当前key的hash与位置已有元素key的hash相同并且equals也相同,则此时将key与value替代掉此位置原有元素 并返回原有key
- put方法为synchronized
public synchronized V put(K key, V value) {
// Make sure the value is not null
//判断key是否为null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
//将哈希表数据赋值给tab数组
Entry<?,?> tab[] = table;
//获取hash值
int hash = key.hashCode();
//获取一个小标
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
//根据下标获取元素
Entry<K,V> entry = (Entry<K,V>)tab[index];
//元素不为空走循环方法
for(; entry != null ; entry = entry.next) {
//判断key是否与原有位置key相同
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
//不为空或没有进循环中的if则调用添加方法
addEntry(hash, key, value, index);
return null;
}
get()方法逻辑如下
- 将哈希表数据赋值给一个变量
- 对传入的key进行散列,并获取下标
- 拿传入key的hash与数据进行循环比对,如hash相同并且equals也相同则返回value
代码如下:
public synchronized V get(Object key) {
//将哈希表数据给到
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
//获取数据比对
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
rehash()方法逻辑如下
- 长度直接扩充到原有两倍加一 ,如果扩充完之后大于长度允许的最大值,则长度变为最大长度
- 进行重新计算散列值添加
代码如下:
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//newCapacity 为扩充完大小
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<?,?>[] 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;
}
}
}
remove(Object key)逻辑如下
- 获取传入key的hash计算出下标
- 进行比对如有相同删除
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//根据key的hash获取下标
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
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;
}
至此HahsTable介绍完毕,面试官问还害怕嘛?