1.数据结构
HashTable的数据结构和HashMap的数据结构相同,都是有散列表构成的,内部都是由Entry<K,V>来维护的。
2.主要参数
private transient Entry<K,V>[] table;//数据结构
private transient int count;// Entry<K,V>[] table已用长度(每次创建一个Entry,count+1) private int threshold;//用于扩容比价的临界值(和已用容量进行比较) private float loadFactor;//负载因子 private transient int modCount = 0; static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;//最大容量 |
3.HashTable的核心构造方法
public Hashtable() { this(11, 0.75f); } 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);
if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; //初始化的时候就创建Entry类型数组table table = new Entry[initialCapacity]; //用于扩容的临界值(和已用容量进行比较) threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); initHashSeedAsNeeded(initialCapacity); } |
3.1初始化容量
Hashtable的initialCapacity(初始化容量默认11)loadFactor(负载因子0.75f);
初始化时赋值threshold的为扩展比较初始容量(0.75f*initialCapacity)和最大容量Integer.MAX_VALUE中较小的一个
(HashTable的容量不足时用于对容量扩展)
3.HashTable的Put方法
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { //HashTable不可以存Null值 throw new NullPointerException(); }
// Makes sure the key is not already in the hashtable. 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)) { V old = e.value; e.value = value; return old; } }
modCount++; if (count >= threshold) { //默认空间不足容量的75%开始扩容 // 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; } |
HashTable中的方法都使用了关键字sychronized修饰。所以是线程安全的。
并且HashTable的不可以存NULL值
通过参数key的hash值进行(hash & 0x7FFFFFFF)% tab.length运算得到数组的位置进行添加
3.1扩容方法rehash();
//默认空间不足容量的75%开始扩容
//每次扩大到原来的容量的2倍
protected void rehash() { int oldCapacity = table.length;//table长度 Entry<K,V>[] oldMap = table;
// overflow-conscious code int newCapacity = (oldCapacity << 1) + 1; //长度扩大到原来的的倍+1 最大为MAX_ARRAY_SIZE 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循环.外层循环桶位,内层循环链表元素,对每个元素进行重新计算在新数组中的桶位 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; } } } |
Rehash(扩容方法)首先新建一个Entry<?,?> oldMap数组对象.
最重要的是其中的两层for循环.外层循环桶位,内层循环链表元素,对每个元素进行重新计算在新数组中的桶位.
(扩容机制是把所有元素重新计算桶位,放入新的Entry中)
int index = (e.hash & 0x7FFFFFFF) % newCapacity 由于Hash Table的桶位基于最大容量,每次扩容都要重新计算一遍桶位
4.HashTable的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; } |
hashtable的寻找方式是(hash &0x7FFFFFFF)% tab.length,其实就是. hash值对table中元素个数的求模运算
5.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; } |
这个方法也很简单.先通过hash值计算得出桶位,然后遍历桶位上的链表.从链表中remove掉目标元素.
6.总结:
1..维护Hashtable内部的是一个Entry,Hashmap的默认容量为11,负载因子为0.75,
2.扩容方式当Entry已用空间>=总空间的75%,总空间扩大到原来的2倍。(因为每次扩容都远计算桶位,这样扩容可以减少扩容次数)
3.Hashtable所有的方法都有synchronized修饰线程安全
4. Hashtable不可以存Null值,
5.HashTable的容量指的是Entry<K,V>[] table的长度
6.计算桶位的方式int index= (hash & 0x7FFFFFFF) % tab.length;