HashTable学习笔记
1、Hashtable简单介绍
HashTable是线程安全的数据结构,多个线程可以共享一个HashTable
HashTable的Key和Value都不允许为null
HashTable底层用数组和链表进行实现
HashTable默认容量为11,负载因子为0.75
HashTable每次扩容大小为 *2+1
HashTable的遍历通过Enumeration
HashMap是通过”拉链法”实现的哈希表。它包括几个重要的成员变量: table, size, threshold, loadFactor, modCount。
①table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的”key-value键值对”都是存储在Entry数组中的。
②size是HashMap的大小,它是HashMap保存的键值对的数量。
③threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值=“容量*负载因子”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
④loadFactor就是负载因子。
⑤modCount是用来实现fail-fast机制的。
2、HashTable的四种构造函数
1、public Hashtable(int initialCapacity, float loadFactor)
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;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
指定初始容量,和负载因子
2、public Hashtable(int initialCapacity)
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
指定初始化容量,使用默认的负载因子0.75
3、public Hashtable()
public Hashtable() {
this(11, 0.75f);
}
无参构造函数,使用默认容量11和默认的初始化影子0.75
4、public Hashtable(Map<? extends K, ? extends V> t)
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
创造一个包含Map的子类
3、HashTable的原理
简单来说,HashTable的原理和HashMap是一样的,HashMap是HashTable的轻量级实现
有关原理部分可以看我有关HashMap的笔记HashMap的学习笔记
4、HashTable几个方法详解
1、get方法
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;
}
①HashTable的get方法是同步的,因为它加了锁
②我们可以看到它求数组下表的方式是(hash & 0x7FFFFFFF) % tab.length
0x7FFFFFFF是一个质数,这样可以保证(hash & 0x7FFFFFFF)进行于运算后出来一个正整数,不会产生数组下表的异常
③当找到数组下表后,就是常见的循环链表判断操作,直到找出这个数
2、put
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;
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) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
put方法也非常容易理解,主要就是判断了Key和Value不允许为空,然后同样使用拉链法
先计算hash确定位置,然后使用链表