副标题#e#
Hashtable简介
Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表办理斗嘴问题,容量不敷(高出了阀值)时,同样会自动增长。
Hashtable也是JDK1.0引入的类,是线程安详的,能用于多线程情况中。
Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。
HashTable源码分解
Hashtable的源码的许多实现都与HashMap差不多,源码如下(插手了较量具体的注释):
package java.util;
import java.io.*;
public class Hashtableextends Dictionaryimplements Map, Cloneable, java.io.Serializable {
// 生存key-value的数组。
// Hashtable同样回收单链表办理斗嘴,每一个Entry本质上是一个单向链表
private transient Entry[] table;
// Hashtable中键值对的数量
private transient int count;
// 阈值,用于判定是否需要调解Hashtable的容量(threshold = 容量*加载因子)
private int threshold;
// 加载因子
private float loadFactor;
// Hashtable被改变的次数,用于fail-fast机制的实现
private transient int modCount = 0;
// 序列版本号
private static final long serialVersionUID = 1421746759512286392L;
// 指定“容量巨细”和“加载因子”的结构函数
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)(initialCapacity * loadFactor);
}
// 指定“容量巨细”的结构函数
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
// 默认结构函数。
public Hashtable() {
// 默认结构函数,指定的容量巨细是11;加载因子是0.75
this(11, 0.75f);
}
// 包括“子Map”的结构函数
public Hashtable(Map t) {
this(Math.max(2*t.size(), 11), 0.75f);
// 将“子Map”的全部元素都添加到Hashtable中
putAll(t);
}
public synchronized int size() {
return count;
}
public synchronized boolean isEmpty() {
return count == 0;
}
// 返回“所有key”的列举工具
public synchronized Enumerationkeys() {
return this.getEnumeration(KEYS);
}
// 返回“所有value”的列举工具
public synchronized Enumerationelements() {
return this.getEnumeration(VALUES);
}
// 判定Hashtable是否包括“值(value)”
public synchronized boolean contains(Object value) {
//留意,Hashtable中的value不能是null,
// 若是null的话,抛出异常!
if (value == null) {
throw new NullPointerException();
}
// 从后向前遍历table数组中的元素(Entry)
// 对付每个Entry(单向链表),逐个遍历,判定节点的值是否便是value
Entry tab[] = table;
for (int i = tab.length ; i-- > 0 ;) {
for (Entrye = tab[i] ; e != null ; e = e.next) {
if (e.value.equals(value)) {
return true;
}
}
}
return false;
}
public boolean containsValue(Object value) {
return contains(value);
}
// 判定Hashtable是否包括key
public synchronized boolean containsKey(Object key) {
Entry tab[] = table;
//计较hash值,直接用key的hashCode取代
int hash = key.hashCode();
// 计较在数组中的索引值
int index = (hash & 0x7FFFFFFF) % tab.length;
// 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
for (Entrye = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return true;
}
}
return false;
}
// 返回key对应的value,没有的话返回null
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
// 计较索引值,
int index = (hash & 0x7FFFFFFF) % tab.length;
// 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
for (Entrye = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
// 调解Hashtable的长度,将长度酿本钱来的2倍+1
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
//建设新容量巨细的Entry数组
int newCapacity = oldCapacity * 2 + 1;
Entry[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int)(newCapacity * loadFactor);
table = newMap;
//将“旧的Hashtable”中的元素复制到“新的Hashtable”中
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entryold = oldMap[i] ; old != null ; ) {
Entrye = old;
old = old.next;
//从头计较index
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}
// 将“key-value”添加到Hashtable中
public synchronized V put(K key, V value) {
// Hashtable中不能插入value为null的元素!!!
if (value == null) {
throw new NullPointerException();
}
// 若“Hashtable中已存在键为key的键值对”,
// 则用“新的value”替换“旧的value”
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entrye = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
// 若“Hashtable中不存在键为key的键值对”,
// 将“修改统计数”+1
modCount++;
// 若“Hashtable实际容量” > “阈值”(阈值=总的容量 * 加载因子)
// 则调解Hashtable的巨细
if (count >= threshold) {
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
//将新的key-value对插入到tab[index]处(即链表的头结点)
Entrye = tab[index];
tab[index] = new Entry(hash, key, value, e);
count++;
return null;
}
// 删除Hashtable中键为key的元素
public synchronized V remove(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
//从table[index]链表中找出要删除的节点,并删除该节点。
//因为是单链表,因此要保存带删节点的前一个节点,才气有效地删除节点
for (Entrye = 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;
}
// 将“Map(t)”的中全部元素逐一添加到Hashtable中
public synchronized void putAll(Map t) {
for (Map.Entry e : t.entrySet())
put(e.getKey(), e.getValue());
}
// 清空Hashtable
// 将Hashtable的table数组的值全部设为null
// 本栏目
#p#副标题#e#1、二者的存储布局息争决战嘴的要领都是沟通的。#p#分页标题#e#2、HashTable在不指定容量的环境下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量必然要为2的整数次幂,而HashMap则要求必然为2的整数次幂。#p#分页标题#e#3、Hashtable中key和value都不答允为null,而HashMap中key和value都答允为null(key只能有一个为null,而value则可以有多个为null)。可是假如在Hashtable中有雷同put(null,null)的操纵,编译同样可以通过,因为key和value都是Object范例,但运行时会抛出NullPointerException异常,这是JDK的类型划定的。我们来看下ContainsKey要领和ContainsValue的源码:// 判定Hashtable是否包括“值(value)”
public synchronized boolean contains(Object value) {
//留意,Hashtable中的value不能是null,
// 本栏目4、Hashtable扩容时,将容量变为本来的2倍加1,而HashMap扩容时,将容量变为本来的2倍。5、Hashtable计较hash值,直接用key的hashCode(),而HashMap从头计较了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目标是为了将负的hash值转化为正值,因为hash值有大概为负数,而&0x7FFFFFFF后,只有标记外改变,尔后头的位都稳定。
