目录
------分析基于jdk1.8.
简介
Hashtable是jdk1.0引入,与HashMap一样,是用散列表(哈希表)实现的,存储的是键-值对映射。Hashtable继承了抽象类Dictionary,实现了Map、Cloneable、java.io.Serializable接口,所以可以克隆,进行序列化传输。
需要注意的是Hashtable是不允许存储null值和null键(会抛出NullPointerException异常),Hashtable中大部分的方法都使用了synchronized修改,所以是线程安全的,可用于多线程环境。
与其他的映射集一样,Hashtable中也存在哈希碰撞(哈希冲突)的情况,解决方法是拉链法,即哈希桶数组加链表,其中链表 是单向的。从源码可以看到其实链表中节点是一个内部类Entry,继承了接口Map.Entry。包含了键的哈希值,键和值以及下一个节点Entry的引用。实际上Entry就是用于存储键-值对映射的类,Hashtable内部存储数据的容器就是一个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;
}
}
介绍
1.构造方法
//构建一个指定的初始容量和负载因子的Hashtable
public Hashtable(int initialCapacity, float loadFactor) {}
//构建一个指定初始容量和默认负载因子为0.75的Hashtable
public Hashtable(int initialCapacity) {}
//构建一个默认初始容量为11,默认负载因子为0.75的Hashtable.
public Hashtable() {}
//构建一个与指定Map具有相同映射关系的Hashtable
public Hashtable(Map<? extends K, ? extends V> t) {}
2.内部变量
//Hashtable用于保存键-值对映射的数组
private transient Entry<?,?>[] table;
//Hashtable中键-值对映射数量
private transient int count;
//阈值(capacity*loadFactor),Hashtable的实际元素数量超过此值将会扩容
private int threshold;
//负载因子
private float loadFactor;
//Hashtable修改的次数,用于fail-fast机制
private transient int modCount = 0;
对于Hashtable中几个变量说明一下:
table是Hashtable用于存储数据的容量,实际就是一个Entry类型数组,数组每个索引保存的是节点Entry,由于每个节点保存了后面节点的引用,所以实际上每个索引位置都是一条单向链表。
thresold是阈值,通过capacity乘以loadFactor得到的,容量超过此值时将会扩容为原先的2倍+1。
loadFactor是负载因子,表示的是哈希桶数组在扩容之前键-值对的充满程度。默认值是0.75。
modCount是用来记录修改的次数,任何对Hashtable的修改都会导致ModCount++,使用迭代器进行迭代时会将ModCount值赋值给expectedModCount。在迭代过程中会判断modCount是否等于expectedModCount。如果不等,说明在迭代过程中其他线程修改了Hashtable,就会抛出ConcurrentModificationException异常。所谓的是fail-fast机制。
3.内部方法
//返回Hashtable中键的数量
public synchronized int size() {}
//如果Hashtable中不包含任何的键-值映射,将会返回true.
public synchronized boolean isEmpty() {}
//返回迭代Hashtable中所有键的Enumeration对象。
public synchronized Enumeration<K> keys() {}
//返回迭代Hashtable中所有值的Enumeration对象。
public synchronized Enumeration<V> elements() {}
//如果Hashtable中包含指定的值,将会返回true.如果指定的值value为null时,将会抛出NullPointerException异常。
public synchronized boolean contains(Object value) {}
//如果Hashtable包含指定值,将会返回true.如果指定值为null,将抛出NullPointerException异常。
public boolean containsValue(Object value) {}
//如果Hashtable中包含指定的键,将返回true.如果将为null,将抛出NullPointerException异常
public synchronized boolean containsKey(Object key) {}
//返回Hashtable中指定键key对应映射的值.如果指定键为null,将会抛出NullPointerException异常
public synchronized V get(Object key) {}
//将指定的键-值对映射添加到Hashtable。如果指定的"key-value"对映射中其中一个为null,将抛出NullPointerException异常。
public synchronized V put(K key, V value) {}
//从Hashtable中移除指定键对应的键-值对映射.如果指定的键为null,将会抛出NullPointerException异常
public synchronized V remove(Object key) {}
//将映射集合t中所有的键-值对映射添加到Hashtable中.如果指定映射集合t为null,将会抛出NullPointerException异常
public synchronized void putAll(Map<? extends K, ? extends V> t) {}
//清空Hastable
public synchronized void clear() {}
//创建Hashtable的浅度复制
public synchronized Object clone() {}
//返回Hashtable对象的字符串表现形式
public synchronized String toString() {}
//返回Hashtable中所有的键集合set
public Set<K> keySet() {}
//返回Hashtable中所有键-值对的集合Set
public Set<Map.Entry<K,V>> entrySet() {}
//返回Hashtable的值集合
public Collection<V> values() {}
//此Hashtable是否与指定对象o相等
public synchronized boolean equals(Object o) {}
//计算Hashtable中所有Entry哈希值的总和。
public synchronized int hashCode() {}
//返回Hashtable中指定键key对应的值value,如果value为null,返回指定的默认值defaultValue
public synchronized V getOrDefault(Object key, V defaultValue) {}
//对这个Map中所有的键和值执行action.
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {}
//用键-值对在给指定函数上的执行结果替换键-值对的值.
public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {}
//Hashtable中键没有映射关系情况下与指定的值进行关联.
public synchronized V putIfAbsent(K key, V value) {}
//从Hashtable中移除与指定key和指定value同时相等的键-值对映射
public synchronized boolean remove(Object key, Object value) {}
//如果Hashtable中存在与指定键和指定值oldValue相等的映射,用newValue替换映射的值
public synchronized boolean replace(K key, V oldValue, V newValue) {}
//如果Hashtable中存在于指定键的映射,用指定的value替换映射的值
public synchronized V replace(K key, V value) {}
//如果指定key不存在映射关系,根据key计算出的函数值,将key与函数值进行关联.
public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {}
//如果Hashtable中存在指定键key的映射,根据key和key映射的值计算出的函数值,函数值不为空时,将key与函数值关联.
//函数值为空时,将会删除指定键key的映射
public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {}
//如果Hashtable中存在指定键key的映射,根据key和key映射的值计算出的函数值,函数值不为空时,将key与函数值关联.函数值为空时,删除指定键key的映射;
//如果Hashtable中不存在指定键的映射,当函数值不为空时,向Hashtable中添加键-函数值映射
public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {}
//如果Hashtable中存在指定键key的映射时,根据键对应的值和指定的值计算函数表达式的函数值,如果函数值为空,删除键的映射,如果函数值不为空,将键-函数值关联。
//如果Hashtble中不存在指定键key的映射时,并且指定的值value不为null,将键-指定值value映射添加到Hashtable中
public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {}
源码分析(jdk1.8)
关于源码几点说明
1.Hashtable的键和值都不能为null,如果为null时,大部分方法里面处理方式会判断如果值为null,将会抛出NullPointerException异常。如果键为null时,调用key.hashCode()方法将会抛出NullPointerException异常。
2.key.hashCode()获取键的哈希值,哈希值与数组长度取模是直接通过 (hash & 0x7FFFFFFF) % tab.length,由于计算机处理数据是基于二进制的,算术运算术"%"效率较低。
3.哈希桶数组中元素的数量超过阈值,将会扩容,根据int newCapacity = (oldCapacity << 1) + 1计算得到扩容后容量=原先容量*2+1。
4.大部分的方法都有定位键在哈希桶数组的位置代码,大致思路:首先是获取键的哈希值,然后键的哈希值与数组长度取模,得到在数组中的索引位置,遍历索引位置的链表来定位节点(目标节点Entry与指定键具有相同的hash值和键值)
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable {
//Hashtable用于保存键-值对映射的数组
//每个Entry内部保存了下个节点next引用,所以数组table中所有索引位置都是单向链表。
private transient Entry<?,?>[] table;
//Hashtable中键-值对映射数量
private transient int count;
//阈值(capacity*loadFactor),Hashtable的实际元素数量超过此值将会扩容
private int threshold;
//负载因子
private float loadFactor;
//Hashtable的结构被修改的次数(即键-值对映射数量变化或者rehash操作)
//变量用于fail-fast机制
private transient int modCount = 0;
//序列化版本号
private static final long serialVersionUID = 1421746759512286392L;
//构建一个指定的初始容量和负载因子的Hashtable
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类型的数组
table = new Entry<?,?>[initialCapacity];
//获取阈值的大小(initialCapacity * loadFactor)
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
//构建一个指定初始容量和负载因子默认值为0.75的Hashtable.
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
//构建一个默认初始容量为11,负载因子默认值为0.75的Hashtable.
public Hashtable() {
this(11, 0.75f);
}
//构建一个与指定Map具有相同映射关系的Hashtable
//如果指定Map为null,将会抛出NullPointerException异常。
public Hashtable(Map<? extends K, ? extends V> t) {
//首先会创建一个负载因子是0.75,容量能容纳指定映射的Hashtable
this(Math.max(2*t.size(), 11), 0.75f);
//然后调用putAll()方法将映射t中键-值对映射存储到Hashtable中
putAll(t);
}
//返回Hashtable中键的数量
public synchronized int size() {
return count;
}
//如果Hashtable中不包含任何的键-值映射,将会返回true.
public synchronized boolean isEmpty() {
return count == 0;
}
//返回迭代Hashtable中所有键的Enumeration对象。
public synchronized Enumeration<K> keys() {
return this.<K>getEnumeration(KEYS);
}
//返回迭代Hashtable中所有值的Enumeration对象。
public synchronized Enumeration<V> elements() {
return this.<V>getEnumeration(VALUES);
}
//如果Hashtable中包含指定的值,将会返回true.
//如果指定的值value为null时,将会抛出NullPointerException异常。
//此方法比containKey方法操作成本高
public synchronized boolean contains(Object value) {
if (value == null) {
throw new NullPointerException();
}
Entry<?,?> tab[] = table;
//反向迭代数组table
for (int i = tab.length ; i-- > 0 ;) {
//正向迭代table指定索引位置的单向链表
for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
//如果链表中节点Entry的值等于value,返回true.
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
//如果Hashtable包含指定值,将会返回true.
//如果指定值为null,将抛出NullPointerException异常。
public boolean containsValue(Object value) {
return contains(value);
}
//如果Hashtable中包含指定的键,将返回true.
//如果将为null,将抛出NullPointerException异常
public synchronized boolean containsKey(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
//键的hash值取模,得到数组table中索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
//迭代指定索引位置index的链表
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
//如果链表中存在与指定键具有相同的hash值和键值的节点Entry,返回true.
if ((e.hash == hash) && e.key.equals(key)) {
return true;
}
}
return false;
}
//返回Hashtable中指定键key对应映射的值
//如果指定键为null,将会抛出NullPointerException异常
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
//指定键key的hash值
int hash = key.hashCode();
//键的hash值与数组长度取模,得到在数组table中索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
//迭代指定索引位置index的链表
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
//如果链表中存在与指定键具有相同的hash值和键值的节点Entry,返回节点的值.
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
//可分配数组的最大值,虚拟机在数组中预留一些空间,如果数组的容量超过此最大值,将会引起OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//Hashtable扩容并调整内部元素位置
//此方法会在键数量超过阈值(capacity*loadFactor)将会自动调整
//容量会变为原先的2倍+1
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
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];
//修改次数+1
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;
//键-值对Entry的哈希值与扩容后的容量取模,得到在新数组中的索引位置
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
//将后面添加的节点添加到之前的节点前面
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
//向Hashtable中添加键-值对映射
private void addEntry(int hash, K key, V value, int index) {
//修改次数+1
modCount++;
Entry<?,?> tab[] = table;
//如果Hashtable中实际容量超过了阈值,会调整Hashtable的大小
//并且会重新计算索引位置index
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
//键的hash值
hash = key.hashCode();
//键的hash值与数组长度取模,得到在Hashtable中的索引位置
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
//获取指定索引位置的首节点
Entry<K,V> e = (Entry<K,V>) tab[index];
//新建节点,将新节点添加到已存在节点的前面
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
//将指定的键-值对映射添加到Hashtable。
//如果指定的"key-value"对映射中其中一个为null,将抛出NullPointerException异常。
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;
//指定键的hash值
int hash = key.hashCode();
//键的hash值与数组长度取模,得到在数组table中的索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//遍历指定索引位置的链表
for(; entry != null ; entry = entry.next) {
//如果链表中存在与指定键的hash值和key相同的节点Entry
if ((entry.hash == hash) && entry.key.equals(key)) {
//获取原先的节点entry的值
V old = entry.value;
//用指定的值value替换原先值
entry.value = value;
//返回原先值
return old;
}
}
//走到这里表明在指定索引位置上的链表中不存在与指定的键-值对映射
//向Hashtable指定索引位置添加节点
addEntry(hash, key, value, index);
return null;
}
//从Hashtable中移除指定键对应的键-值对映射,如果不存在指定键,方法不会做任何工作
//方法将返回指定键key对应的值,不存在键-值映射,将返回null
//如果指定的键为null,将会抛出NullPointerException异常
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
//键的hash值
int hash = key.hashCode();
//键的hash值与数组长度取模,得到在数组table中的索引位置
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) {
//如果链表中存在与指定键具有相同的hash值和键值的节点,删除节点
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
//将当前节点的上个节点后驱指针指向当前节点的下个节点
if (prev != null) {
prev.next = e.next;
} else {
//如果是首节点,将下个节点直接存储到指定索引位置
tab[index] = e.next;
}
//元素数量自减
count--;
//获取节点的值value
V oldValue = e.value;
//将节点的值置为null,垃圾回收
e.value = null;
//返回原先值
return oldValue;
}
}
return null;
}
//将映射集合t中所有的键-值对映射添加到Hashtable中
//Hashtable存在与映射集合t相同的键-值对映射,将会用指定映射集合t中的映射替换Hashtable中的映射
//如果指定映射集合t为null,将会抛出NullPointerException异常
public synchronized void putAll(Map<? extends K, ? extends V> t) {
//遍历映射集合t,将所有键-值对映射添加到Hashtable
for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
put(e.getKey(), e.getValue());
}
//清空Hastable
public synchronized void clear() {
Entry<?,?> tab[] = table;
//修改次数自增
modCount++;
//遍历整个数组,将所有索引位置的元素置为null
for (int index = tab.length; --index >= 0; )
tab[index] = null;
//元素数量置为0
count = 0;
}
//创建Hashtable的浅度复制
public synchronized Object clone() {
try {
Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
t.table = new Entry<?,?>[table.length];
for (int i = table.length ; i-- > 0 ; ) {
t.table[i] = (table[i] != null)
? (Entry<?,?>) table[i].clone() : null;
}
t.keySet = null;
t.entrySet = null;
t.values = null;
t.modCount = 0;
return t;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//返回Hashtable对象的字符串表现形式
public synchronized String toString() {
int max = size() - 1;
if (max == -1)
return "{}";
StringBuilder sb = new StringBuilder();
Iterator<Map.Entry<K,V>> it = entrySet().iterator();
sb.append('{');
//遍历键-值对Entry,将键和值都添加到格式中
for (int i = 0; ; i++) {
Map.Entry<K,V> e = it.next();
K key = e.getKey();
V value = e.getValue();
sb.append(key == this ? "(this Map)" : key.toString());
sb.append('=');
sb.append(value == this ? "(this Map)" : value.toString());
if (i == max)
return sb.append('}').toString();
sb.append(", ");
}
}
//返回Enumeration对象
private <T> Enumeration<T> getEnumeration(int type) {
if (count == 0) {
return Collections.emptyEnumeration();
} else {
return new Enumerator<>(type, false);
}
}
//返回Iterator对象
private <T> Iterator<T> getIterator(int type) {
if (count == 0) {
return Collections.emptyIterator();
} else {
return new Enumerator<>(type, true);
}
}
// Views
//Hashtable中所有键的集合,由于键本身不会重复,所以是Set集合
private transient volatile Set<K> keySet;
//Hashtable中所有键-值的集合,键-值对不会重复,所以是Set集合
private transient volatile Set<Map.Entry<K,V>> entrySet;
//Hashtabe中所有值的集合,值没有要求,可以重复
private transient volatile Collection<V> values;
//返回Hashtable中所有的键集合set
//此set集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear)
//不支持add或者addAll操作,移除操作将直接影响Hashtable。
public Set<K> keySet() {
if (keySet == null)
keySet = Collections.synchronizedSet(new KeySet(), this);
return keySet;
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return getIterator(KEYS);
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return Hashtable.this.remove(o) != null;
}
public void clear() {
Hashtable.this.clear();
}
}
//返回Hashtable中所有键-值对的集合Set
// 此set集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear),
//不支持add或者addAll操作。移除操作将直接影响Hashtable
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)
entrySet = Collections.synchronizedSet(new EntrySet(), this);
return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return getIterator(ENTRIES);
}
//向键-值对集合中添加节点Entry
public boolean add(Map.Entry<K,V> o) {
return super.add(o);
}
//键-值对集合是否包含指定元素o
//比较的是Entry的hash值和Entry本身是否相等(包含键和值都相等)
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
Object key = entry.getKey();
Entry<?,?>[] tab = table;
//键的哈希值与数组长度取模,得到在数组中索引位置
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
//遍历指定索引位置index处的链表,找到与Entry和Entry的哈希值相同的节点
for (Entry<?,?> e = tab[index]; e != null; e = e.next)
if (e.hash==hash && e.equals(entry))
return true;
return false;
}
//从键-值对集合中移除指定元素o
//如果Entry的hash值和Entry值(包含键和值)都相等,移除命中元素
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
Object key = entry.getKey();
Entry<?,?>[] tab = table;
//获取键的哈希值
int hash = key.hashCode();
//键的哈希值与数组长度取模,得到在数组中索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
//遍历指定索引位置的链表,如果Entry的hash值和Entry值(包含键和值)都相等,移除命中元素
for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
if (e.hash==hash && e.equals(entry)) {
modCount++;
if (prev != null)
//当前节点的上个节点后驱指针指向当前节点的下个节点
prev.next = e.next;
else
//如果是首节点,将下个几点存储到索引位置
tab[index] = e.next;
//元素数量自减
count--;
e.value = null;
return true;
}
}
return false;
}
//键-值对数量
public int size() {
return count;
}
//清空数组
public void clear() {
Hashtable.this.clear();
}
}
//返回Hashtable的值集合
//集合支持键的移除操作(Iterator.remove,Set.remove,removeAll,retainAll,clear),
//不支持add或者addAll操作。移除操作将会影响Hashtable
public Collection<V> values() {
if (values==null)
values = Collections.synchronizedCollection(new ValueCollection(),
this);
return values;
}
private class ValueCollection extends AbstractCollection<V> {
public Iterator<V> iterator() {
return getIterator(VALUES);
}
public int size() {
return count;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
Hashtable.this.clear();
}
}
// Comparison and hashing
//此Hashtable是否与指定对象o相等
public synchronized boolean equals(Object o) {
//同一个引用,直接返回true
if (o == this)
return true;
//不是同一个类型,返回false
if (!(o instanceof Map))
return false;
Map<?,?> t = (Map<?,?>) o;
//元素数量不相同,返回false
if (t.size() != size())
return false;
try {
//遍历Hashtable,查看所有的键-值对映射是否也存在于指定对象o中。
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Map.Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(t.get(key)==null && t.containsKey(key)))
return false;
} else {
if (!value.equals(t.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
//计算Hashtable中所有Entry哈希值的总和。
//如果Hashtable中元素的数量是0或者负载因子是小于0,返回0
//否则将返回所有Entry的哈希值的总和
public synchronized int hashCode() {
/*
* This code detects the recursion caused by computing the hash code
* of a self-referential hash table and prevents the stack overflow
* that would otherwise result. This allows certain 1.1-era
* applets with self-referential hash tables to work. This code
* abuses the loadFactor field to do double-duty as a hashCode
* in progress flag, so as not to worsen the space performance.
* A negative load factor indicates that hash code computation is
* in progress.
*/
int h = 0;
if (count == 0 || loadFactor < 0)
return h; // Returns zero
loadFactor = -loadFactor; // Mark hashCode computation in progress
Entry<?,?>[] tab = table;
//迭代Hashtable,将所有Entry的哈希值相加
for (Entry<?,?> entry : tab) {
while (entry != null) {
h += entry.hashCode();
entry = entry.next;
}
}
loadFactor = -loadFactor; // Mark hashCode computation complete
return h;
}
@Override
//返回Hashtable中指定键key对应的值value,如果value为null,返回指定的默认值defaultValue
public synchronized V getOrDefault(Object key, V defaultValue) {
V result = get(key);
return (null == result) ? defaultValue : result;
}
@SuppressWarnings("unchecked")
@Override
//对这个Map中所有的键和值执行action操作.
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action); // explicit check required in case
// table is empty.
final int expectedModCount = modCount;
Entry<?, ?>[] tab = table;
//遍历Entry数组
for (Entry<?, ?> entry : tab) {
while (entry != null) {
action.accept((K)entry.key, (V)entry.value);
entry = entry.next;
//检查并发修改
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
}
@SuppressWarnings("unchecked")
@Override
//用键-值对在给指定函数上的执行结果替换键-值对的值.
public synchronized void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function); // explicit check required in case
// table is empty.
final int expectedModCount = modCount;
Entry<K, V>[] tab = (Entry<K, V>[])table;
//遍历数组,用键和值在指定函数上的执行结果替换值
for (Entry<K, V> entry : tab) {
while (entry != null) {
entry.value = Objects.requireNonNull(
function.apply(entry.key, entry.value));
entry = entry.next;
//检查并发修改
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
}
}
}
@Override
//Hashtable中键没有映射关系情况下与指定的值进行关联.
public synchronized V putIfAbsent(K key, V value) {
Objects.requireNonNull(value);
// 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) {
//存在与指定键具有相同的hash值和键值的节点,值为空时,将键与指定的值value关联,不为空时,返回原先的值
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
if (old == null) {
entry.value = value;
}
return old;
}
}
//将关联后的键-值对添加到Hashtable中
addEntry(hash, key, value, index);
return null;
}
@Override
//从Hashtable中移除与指定key和指定value同时相等的键-值对映射
public synchronized boolean remove(Object key, Object value) {
Objects.requireNonNull(value);
Entry<?,?> tab[] = table;
//获取键的哈希值
int hash = key.hashCode();
//键的哈希值与数组长度取模,得到在数组中索引位置
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) {
//如果键的哈希值,键和值与指定的key和value都相同,删除命中节点
if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
modCount++;
//将当前节点的上个节点后驱指针指向当前节点的下个节点
if (prev != null) {
prev.next = e.next;
} else {
//如果是首节点,将下个节点存储到索引位置
tab[index] = e.next;
}
count--;
e.value = null;
return true;
}
}
return false;
}
@Override
//如果Hashtable中存在与指定键和指定值oldValue相等的映射,用newValue替换映射的值
public synchronized boolean replace(K key, V oldValue, V newValue) {
Objects.requireNonNull(oldValue);
Objects.requireNonNull(newValue);
Entry<?,?> tab[] = table;
//获取键的哈希值
int hash = key.hashCode();
//键的哈希值与数组长度取模,得到在数组中的索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
for (; e != null; e = e.next) {
//如果键的哈希值和键值与指定键key的相同
//并且值与指定的值oldValue相同,则用newValue替换指定键对应的值
if ((e.hash == hash) && e.key.equals(key)) {
if (e.value.equals(oldValue)) {
e.value = newValue;
return true;
} else {
return false;
}
}
}
return false;
}
@Override
//如果Hashtable中存在于指定键的映射,用指定的value替换映射的值
public synchronized V replace(K key, V value) {
Objects.requireNonNull(value);
Entry<?,?> tab[] = table;
//获取键的哈希值
int hash = key.hashCode();
//键的哈希值与数组长度取模,得到在数组中索引位置
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
//遍历指定索引位置链表
for (; e != null; e = e.next) {
//如果键的哈希值,键值与指定键的相同,用指定的值value替换映射的值
if ((e.hash == hash) && e.key.equals(key)) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
return null;
}
@Override
//如果指定key不存在映射关系,根据key计算出的函数值,将key与函数值进行关联.
public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
//遍历指定索引位置的链表,找到符合条件的键并返回键对应的值
for (; e != null; e = e.next) {
if (e.hash == hash && e.key.equals(key)) {
// Hashtable not accept null value
return e.value;
}
}
//键不存在映射时,根据key计算函数表达式的函数值,并将函数值和键添加到Hashtable中
V newValue = mappingFunction.apply(key);
if (newValue != null) {
addEntry(hash, key, newValue, index);
}
return newValue;
}
@Override
//如果Hashtable中存在指定键key的映射,根据key和key映射的值计算出的函数值,函数值不为空时,将key与函数值关联.
//函数值为空时,将会删除指定键key的映射
public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
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)) {
//根据键和值计算函数表达式的值
V newValue = remappingFunction.apply(key, e.value);
//函数值为空时,删除键-值对映射
if (newValue == null) {
modCount++;
//不是首节点,将当前节点上个节点的后驱指针指向当前节点的下个节点
if (prev != null) {
prev.next = e.next;
} else {
//将首节点的下个节点存储到索引位置
tab[index] = e.next;
}
count--;
//函数不为空时,将键与函数值关联
} else {
e.value = newValue;
}
return newValue;
}
}
return null;
}
@Override
//如果Hashtable中存在指定键key的映射,根据key和key映射的值计算出的函数值,函数值不为空时,将key与函数值关联.函数值为空时,删除指定键key的映射;
//如果Hashtable中不存在指定键的映射,当函数值不为空时,向Hashtable中添加键-函数值映射
public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
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 && Objects.equals(e.key, key)) {
V newValue = remappingFunction.apply(key, e.value);
//函数值为空时,将键-对映射映射删除
if (newValue == null) {
modCount++;
//不是首节点,将当前节点上个节点的后驱指针指向当前节点的下个节点
if (prev != null) {
prev.next = e.next;
} else {
//将首节点的下个节点存储到索引位置
tab[index] = e.next;
}
count--;
} else {
//否则将键与函数值进行关联映射
e.value = newValue;
}
return newValue;
}
}
V newValue = remappingFunction.apply(key, null);
if (newValue != null) {
addEntry(hash, key, newValue, index);
}
return newValue;
}
@Override
//如果Hashtable中存在指定键key的映射时,根据键对应的值和指定的值计算函数表达式的函数值,如果函数值为空,删除键的映射,如果函数值不为空,将键-函数值关联。
//如果Hashtble中不存在指定键key的映射时,并且指定的值value不为null,将键-指定值value映射添加到Hashtable中
public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
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)) {
V newValue = remappingFunction.apply(e.value, value);
//函数值为null时,将键的映射删除
if (newValue == null) {
modCount++;
//不是首节点,将当前节点上个节点的后驱指针指向当前节点的下个节点
if (prev != null) {
prev.next = e.next;
} else {
//将首节点的下个节点存储到索引位置
tab[index] = e.next;
}
count--;
//函数值不为null时,将键与函数关联
} else {
e.value = newValue;
}
return newValue;
}
}
//hashtable中不存在指定键的映射,并且指定值value不为空
//向Hashtable中添加键-指定值value映射
if (value != null) {
addEntry(hash, key, value, index);
}
return value;
}
//将Hashtable的状态写到流中
//依次写入Hashtable的容量,实际元素的数量,以及所有的键-值对映射
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
Entry<Object, Object> entryStack = null;
synchronized (this) {
// Write out the threshold and loadFactor
s.defaultWriteObject();
// Write out the length and count of elements
//写入数组长度和元素数量
s.writeInt(table.length);
s.writeInt(count);
// Stack copies of the entries in the table
//遍历数组,将所有索引位置的链表串成一个单链表
for (int index = 0; index < table.length; index++) {
Entry<?,?> entry = table[index];
//将指定索引位置的链表重新串起来
while (entry != null) {
entryStack =
new Entry<>(0, entry.key, entry.value, entryStack);
entry = entry.next;
}
}
}
// Write out the key/value objects from the stacked entries
//遍历上面单链表,将链表中所有节点的键和值全部写到流中
while (entryStack != null) {
s.writeObject(entryStack.key);
s.writeObject(entryStack.value);
entryStack = entryStack.next;
}
}
//从流中读取数据,重建Hashtable(反序列化)
//依次从流中读取阈值和负载因子、数组容量、元素数量,元素构建Hashtable
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the threshold and loadFactor
s.defaultReadObject();
// Validate loadFactor (ignore threshold - it will be re-computed)
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new StreamCorruptedException("Illegal Load: " + loadFactor);
// Read the original length of the array and number of elements
//读取数组的长度和元素的数量
int origlength = s.readInt();
int elements = s.readInt();
// Validate # of elements
if (elements < 0)
throw new StreamCorruptedException("Illegal # of Elements: " + elements);
// Clamp original length to be more than elements / loadFactor
// (this is the invariant enforced with auto-growth)
origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
// Compute new length with a bit of room 5% + 3 to grow but
// no larger than the clamped original length. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
int length = (int)((elements + elements / 20) / loadFactor) + 3;
if (length > elements && (length & 1) == 0)
length--;
length = Math.min(length, origlength);
// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);
table = new Entry<?,?>[length];
threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
count = 0;
// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// sync is eliminated for performance
reconstitutionPut(table, key, value);
}
}
//readObject方法调用
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
throws StreamCorruptedException
{
if (value == null) {
throw new java.io.StreamCorruptedException();
}
// Makes sure the key is not already in the hashtable.
// This should not happen in deserialized version.
//键的哈希值
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)) {
throw new java.io.StreamCorruptedException();
}
}
// Creates the new entry.
@SuppressWarnings("unchecked")
//获取指定索引处的节点,新建节点的后驱指针指向此节点
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
/**
* Hashtable bucket collision list entry
*/
//Hashtable中链表的节点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;
}
//向节点Entry设置值,如果value为null,将抛出NullPointerException异常
public V setValue(V value) {
if (value == null)
throw new NullPointerException();
V oldValue = this.value;
this.value = value;
return oldValue;
}
//Entry是Hashtable中索引位置的链表中节点
//此Entry与指定的对象o是否相等,实际比较的是键和值是否都相等
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();
}
}
// Types of Enumerations/Iterations
private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
//用于迭代Hashtable的内部类,实现了Enumeration和Iterator接口
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
Entry<?,?>[] table = Hashtable.this.table;
int index = table.length;
Entry<?,?> entry;
Entry<?,?> lastReturned;
int type;
//表示的是Enumrator是一个迭代器Iterator类型还是枚举Enumeraion类型,如果是true,表示的是Iterator
boolean iterator;
//fail-fast机制,Hashtable的迭代器期望得到modCount值,如果此值在迭代时发生了改变
//表明有并发修改的情况
protected int expectedModCount = modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}
//反向迭代,找到不为null的Entry
public boolean hasMoreElements() {
Entry<?,?> e = entry;
int i = index;
Entry<?,?>[] t = table;
/* Use locals for faster loop iteration */
//反向迭代,找到不为null的节点时,跳出循环
while (e == null && i > 0) {
e = t[--i];
}
entry = e;
index = i;
return e != null;
}
@SuppressWarnings("unchecked")
//获取下一个元素
public T nextElement() {
Entry<?,?> et = entry;
int i = index;
Entry<?,?>[] t = table;
/* Use locals for faster loop iteration */
//反向迭代,找到不为null的节点时,跳出循环
while (et == null && i > 0) {
et = t[--i];
}
entry = et;
index = i;
if (et != null) {
Entry<?,?> e = lastReturned = entry;
entry = e.next;
return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
}
throw new NoSuchElementException("Hashtable Enumerator");
}
// Iterator methods
//迭代的方法,实际会调用hasMoreElements()
public boolean hasNext() {
return hasMoreElements();
}
//获取下个元素
public T next() {
//检查并发修改情况
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
//移除迭代器当前位置的元素
public void remove() {
if (!iterator)
throw new UnsupportedOperationException();
if (lastReturned == null)
throw new IllegalStateException("Hashtable Enumerator");
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
synchronized(Hashtable.this) {
//tab指向是的Hashtable中的数组
Entry<?,?>[] tab = Hashtable.this.table;
//lastReturned是调用next()方法进行赋值,这里与数组长度取模得到在数组中索引位置
int index = (lastReturned.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 == lastReturned) {
modCount++;
expectedModCount++;
//如果是首节点,将下个节点存储在索引位置
if (prev == null)
tab[index] = e.next;
else
//当前节点的上个节点后驱指针指向当前节点的下个节点
prev.next = e.next;
//元素数量自减
count--;
lastReturned = null;
return;
}
}
throw new ConcurrentModificationException();
}
}
}
}
迭代Hashtable
1.遍历Hashtable的键-值对。
通过调用entrySet()方法获取Hashtable的键-值对映射集合Set,然后迭代Set集合获取key和value。
2.遍历Hashtable的键。
调用keySet()方法获取Hashtable的键集合Set,然后迭代Set集合获取键,通过调用get()方法获取指定键对应的值。
3.遍历Hashtable的值。
调用values()方法获取Hashtable的值集合Collection,迭代获取所有值。
4.通过Enumeration来获取Hashtable键和值
分别调用方法keys()和elements()方法来获取Hashtable的键和值。
public class AIteratorHashtable {
public static void main(String[] args) {
Hashtable<String,Integer> map = new Hashtable<>();
map.put("first", 1);
map.put("second",2);
map.put("third", 3);
map.put("forth", 4);
iteratorHashtableByEntrySet(map);
iteratorHashtableByKeySet(map);
iteratorHashtableByValueSet(map);
iteratorHashtableByEnumeration(map);
}
//第一种方式迭代键-值对
private static void iteratorHashtableByEntrySet(Map<String,Integer> map) {
Set<Entry<String, Integer>> entrySet = map.entrySet();
//迭代器
Iterator<Entry<String, Integer>> iterator = entrySet.iterator();
while(iterator.hasNext()) {
Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey()+"------"+entry.getValue());
}
//foreach循环
for(Entry<String,Integer> entry:entrySet) {
System.out.println(entry.getKey()+"------"+entry.getValue());
}
}
//第二种方式迭代键集合
private static void iteratorHashtableByKeySet(Map<String,Integer> map) {
Set<String> keySet = map.keySet();
//迭代器
Iterator<String> iterator = keySet.iterator();
while(iterator.hasNext()) {
String key = iterator.next();
System.out.println(key+"-------"+map.get(key));
}
//foreach循环
for(String key:keySet) {
System.out.println(key+"---------"+map.get(key));
}
}
//第三种方式迭代值
private static void iteratorHashtableByValueSet(Map<String,Integer> map) {
Collection<Integer> values = map.values();
//迭代器
Iterator<Integer> iterator = values.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
//forearch循环
for(Integer in:values) {
System.out.println(in);
}
}
//第四种方式迭代值和值
private static void iteratorHashtableByEnumeration(Hashtable<String,Integer> map) {
Enumeration<String> keys = map.keys();
while(keys.hasMoreElements()) {
System.out.println(keys.nextElement());
}
Enumeration<Integer> elements = map.elements();
while(elements.hasMoreElements()) {
System.out.println(elements.nextElement());
}
}
}
Hashtable与HashMap的区别
1.Hashtable自jdk1.0引入的,继承了Dictionary抽象类,HashMap是jdk1.2引入的,继承了AbstractMap抽象类,它们都继承了Map接口。
2.Hashtable中大部分方法都使用了synchronized修改,线程是安全的。而HashMap是线程不安全的。
3.Hashtable不允许存储null值和null键,这在HashMap是可以的。
4.Hashtable扩容后的容量是原先的2倍+1,而HashMap是原先容量2倍。
5.jdk1.8版本HashMap作为了很多优化,默认初始容量是16,扩容后的容量一定是,计算索引位置是通过位运算(n-1)&key.hash(n是数组长度)得到的。而Hashtable默认初始容量是11,通过算术运算符(hash & 0x7FFFFFFF) % tab.length来计算索引位置,此外HashMap引入了红黑树,链表的节点数量超过8个,会将链表转变成红黑树。