public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
//通过Entry数组保存数据
private transient Entry<?,?>[] table;
// 用来统计这个哈希数组已经使用的空间
private transient int count;
// 设置的一个阈值,当超出这个阈值时就要进行扩容
private int threshold;
//加载因子
private float loadFactor;
/* 用来记录hashtable结构更改的次数,此字段用于使哈希表集合视图中的迭代器快速失效。
This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*/
private transient int modCount = 0;
//能够分配的最大的大小,需要小心越界
// OutOfMemoryError: Requested array size exceeds VM limit
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
相对于HashMap来说,有些不同的地方就是用于存储的对象
HashMap使用Node来存储键值对,而HashTable使用Entry对象来进行存储
HashTable继承了 Dictionary这个类。
加载因子和初始容量是影响其性能的两个因素。默认加载因子大小 0.75是时间和空间消耗最小的。
public Hashtable(int initialCapacity, float loadFactor) {
//initialCapacity是初始容量, loadFactor是加载因子
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
// The value of this field is (int)(capacity * loadFactor.)
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
//使用指定的初始容量和默认的加载因子0.75构建一个新的空哈希表
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
// 无参构造 默认初始容量为11,加载因子是0.75 而hashmap默认容量16
public Hashtable() {
this(11, 0.75f);
}
// 构造一个新的哈希表, 具有与给定的map相同的映射。
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
Entry结构: 通过entry结构中的next属性可以知道, hashtable的结构是一个单向链表
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;//用来记录当前entry的地址
final K key;
V value;
Entry<K,V> next;//表明指向的下一个entry
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();
}
}
##### 1.put
public synchronized V put(K key, V value) {
//判断value值是否为空,如果为空则抛出异常,与HashMap不同,
//hashMap允许key,value为空
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
// 确保hashtable中没有重复的key
// 保存当前的table数据
Entry<?,?> tab[] = table;
// 当前key值的哈希值
int hash = key.hashCode();
//确保当前hashtable中没有key值的存在:
/*将当前的哈希值与最大数组长度进行与运算后对数组长度进行求余
获取到key对应数组中的索引值 */
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
//找到entry数组中对应下标的entry对象
Entry<K,V> entry = (Entry<K,V>)tab[index];
//从获取到的entry对象开始循环,查看是否存在有对应的key,hash存在
/* 如果对应下标中已经存在了entry对象,且hash和key值相等,
那么将新值替换旧值,并返回旧值*/
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
//如果对应下标处没有对象,那么添加一个新的entry对象
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
//添加一次,修改的次数增加
modCount++;
//记录下当前的entry数组
Entry<?,?> tab[] = table;
//如果entry数组中的数量已经大于等于了阈值那么进行扩容
if (count >= threshold) {
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
//创建一个新的entry,添加到对应索引处
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
//记录的保存的entry对象的数量增加
count++;
}
protected void rehash() {
// 获取到旧的数组的长度和对象
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code 溢出
// 定义一个新的容量超出旧容量的2倍+1
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数组用于保存数据
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;
}
}
}
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
//计算出对应的索引值
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
//判断对应索引值上的key和hash值是否相等,如果相对,返回对应索引值的对象。
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
1 继承和实现的类不同:hashtable继承了Dictionary类,hashmap继承了AbstractMap类。
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
2 结构不同:HashTable是实现了拉链法的散列表(拉链法只知道是解决哈希冲突具体还没有进行了解)
HashMap是具有数组,链表,红黑树多种结构
3 安全性不同: HashTable 中的puta方法,
public synchronized V put(K key, V value)从源码上可以看出
hashtable是同步的。而hashmap是非同步的。
4内容上不同: HashTable不允许key,value值为空,当value值为空,还会抛出异常,还采取了计算对应的链表中索引值的方式确保key不为空。 而HashMap允许key,value值可以为空。
5 初始容量不同: Hashtable默认是11,Hashmap 默认是16
6 扩充的容量不同:Hashtable是newCapacity = (oldCapacity << 1) + 1,
HashMap是 newCap = oldCap << 1
HashTable是继承了Dictionary类,这是一个抽象类,在这个抽象类中的put方法中声明了key,value值都不能为null.
public abstract
class Dictionary<K,V>
/* Maps the specified key
to the specified
value
in this dictionary. Neither the key nor the
value can be null
.(key,value值都不能为null)*/
abstract public V put(K key, V value);