HashTable底层源码分析
一、准备工作
package com.liu.map_;
import java.util.Hashtable;
import java.util.Map;
public class HashTable_debug {
public static void main(String[] args) {
Map<Object, Object> hashtable = new Hashtable<>();//断点1
for (int i = 1; i < 10; i++) {
hashtable.put(i,"number"+i);//断点2
}
System.out.println(hashtable);
}
}
二、debug
默认构造函数
可以看出,hashtable默认初始化容量未11,默认扩容因子为0.75
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
进入 this(11, 0.75f),有参构造函数
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @exception IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
//若初始化容量为0,则将容量变为1
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
//新建一个 Entry<?,?>[initialCapacity]数组
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
Hashtable$Entry
hashtable中的Entry数组的源码
实现了Map.Entry<K,V>,需要重写其所有方法,所以有getKey和getValue的方法
Entry<K,V> next;仅有一个next结点,没有pre结点,所以为单向链表
/**
* Hashtable bucket collision list entry
*/
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@SuppressWarnings("unchecked")
protected Object clone() {
return new Entry<>(hash, key, value,
(next==null ? null : (Entry<K,V>) next.clone()));
}
// Map.Entry Ops
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
if (value == null)
throw new NullPointerException();
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
(value==null ? e.getValue()==null : value.equals(e.getValue()));
}
public int hashCode() {
return hash ^ Objects.hashCode(value);
}
public String toString() {
return key.toString()+"="+value.toString();
}
}
跳出Hashtale构造函数
Integer装箱
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
put方法
synchronized:线程安全
hashtable的value值不能为空
遍历当前下标中的链表,看是否有key值相等的,若相等,则覆盖
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
//线程安全
public synchronized V put(K key, V value) {
// Make sure the value is not null
//hashtable的值不能为空
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//计算数组下标
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//遍历当前下标中的链表,看是否有key值相等的,若相等,则覆盖
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
addEntry
count初始为0,当count达到8时,满足count >= threshold(8),即为添加第9个元素时,进行扩容
count在加完新结点后才自增
采用头插法将新的结点插入到tab[index]的头部
头插法原理:e的位置为next指针,即为新节点的next指向e
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
//若满足条件,进行扩容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
//e记录当前的链表头结点
Entry<K,V> e = (Entry<K,V>) tab[index];
//创建结点,采用头插法将新的结点插入到tab[index]的头部
//原理 e的位置为next指针,即为新节点的next指向e
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
rehash()
扩容机制:2*oldCapacity+1
计算新的扩容容量:threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
重新计算下标,并将其赋值给新数组
/**
* Increases the capacity of and internally reorganizes this
* hashtable, in order to accommodate and access its entries more
* efficiently. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
*/
@SuppressWarnings("unchecked")
protected void rehash() {
//length表示的是table数组的长度
int oldCapacity = table.length;
//记录table
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//扩容机制:2*oldCapacity+1
int newCapacity = (oldCapacity+1 << 1) + 1;
//容量过大超过Integer.MAX_VALUE - 8
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
//创建并将新容量赋值给newMap
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
//计算新的扩容容量
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
//将新数组赋值给table
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;
}
}
}
add成功
三、总结(重点)
1、hashtable默认初始化容量未11,默认扩容因子为0.75
2、 Entry<K,V> next;仅有一个next结点,没有pre结点,所以为单向链表
3、synchronized:线程安全
4、hashtable的value值不能为空
5、遍历当前下标中的链表,看是否有key值相等的,若相等,则覆盖
6、count初始为0,当count达到8时,满足count >= threshold(8),即为添加第9个元素时,进行扩容
count在加完新结点后才自增
采用头插法将新的结点插入到tab[index]的头部
头插法原理:e的位置为next指针,即为新节点的next指向e
7、扩容机制:2*oldCapacity+1
计算新的扩容容量:threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);