简单说明:
1、上图中虚线且无依赖字样、说明是直接实现的接口
2、虚线但是有依赖字样、说明此类依赖与接口、但不是直接实现接口
3、实线是继承关系、类继承类、接口继承接口
4、实现Serielazable接口、允许使用ObjectInputStream/ObjectOutputStream读取/写入
5、实现Map接口、以键值对形式存储数据
6、实现Cloneable接口、允许克隆Hashtable
7、继承Dictionary、说明Hashtable是键值对形式类、并且键、值都不允许为null。
8、Dictionary依赖Enumeration、Hashtable可以使用Enumeration、Iterator迭代其中元素。
1、 基于哈希表的Map结构的实现
2、线程安全
3、内部映射无序
4、不允许值为null的key和value
1、构造方法
// 默认构造函数。
public Hashtable()
// 指定“容量大小”的构造函数
public Hashtable(int initialCapacity)
// 指定“容量大小”和“加载因子”的构造函数
public Hashtable(int initialCapacity, float loadFactor)
// 包含“子Map”的构造函数
public Hashtable(Map extends K, ? extends V> t)
2、一般方法
synchronized void clear()
synchronized Object clone()
boolean contains(Object value)
synchronized boolean containsKey(Object key)
synchronized boolean containsValue(Object value)
synchronized Enumeration elements()
synchronized Set> entrySet()
synchronized boolean equals(Object object)
synchronized V get(Object key)
synchronized int hashCode()
synchronized boolean isEmpty()
synchronized Set keySet()
synchronized Enumeration keys()
synchronized V put(K key, V value)
synchronized void putAll(Map extends K, ? extends V> map)
synchronized V remove(Object key)
synchronized int size()
synchronized String toString()
synchronized Collection values()
说明:
1、对哈希表要有简单的认识、
2、Hashtable是通过“拉链法”解决哈希冲突的
3、理解Hashtable源码中的关键部分、Entry实体类的行为、属性。Entry的存储方式、Hashtable的扩容方式、Hashtable内部关于获取新的hash code的算法。
4、与遍历相关:可以使用Enumeration、也可以使用Iterator。
5、与容量有关的内容Hashtable的实例有两个参数影响其性能:初始容量和加载因子。容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
6、默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数Hashtable 类的操作中,包括get 和put 操作,都反映了这一点)。初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于Hashtable 所包含的最大条目数除以加载因子,则永远不会发生 rehash 操作。但是,将初始容量设置太高可能会浪费空间。
7、如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
8、如果很多映射关系要存储在 Hashtable 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。
总结 :
1、数据结构:Hashtable是以哈希表的形式存储数据的、并且是通过“拉链法”解决冲突、Hashtable中存储的Entry继承Map.Entry
/** Hashtable中表示节点的实体类、本质是一个单向链表*/
private static class Entry implements Map.Entry {
int hash;
K key;
V value;
Entry next;
protected Entry(int hash, K key, V value, Entry next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
protected Object clone() {
return new Entry(hash, key, value,
(next==null ? null : (Entry) 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 ^ (value==null ? 0 : value.hashCode());
}
public String toString() {
return key.toString()+"="+value.toString();
}
}
2、通过Key的哈希值定位索引的算法:
// 计算索引值, % tab.length 的目的是防止数据越界
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
3、遍历:
a)Enumerator实现了Enumeration和Iterator接口、说明Enumerator同时具有使用Enumeration迭代和使用Iterator迭代功能、
private class Enumerator implements Enumeration, Iterator {
//指向当前Hashtable的table
Entry[] table = Hashtable.this.table;
int index = table.length;
Entry entry = null;
Entry lastReturned = null;
int type;
/**Enumerator是迭代器还是Enumeration的标志、true——Iterator、false——Enumeration*/
boolean iterator;
/** 将Enumerator当作Iterator使用时需要用到的标志fail-fast机制*/
protected int expectedModCount = modCount;
Enumerator(int type, boolean iterator) {
this.type = type;
this.iterator = iterator;
}
// 从table末尾向前查找,直到找到不为null的Entry。
public boolean hasMoreElements() {
Entry e = entry;
int i = index;
Entry[] t = table;
/* Use locals for faster loop iteration */
while (e == null && i > 0) {
e = t[--i];
}
entry = e;
index = i;
return e != null;
}
//获取下一个元素、
public T nextElement() {
Entry et = entry;
int i = index;
Entry[] t = table;
/* Use locals for faster loop iteration */
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方式判断是否有下一个元素
public boolean hasNext() {
return hasMoreElements();
}
//Iterator方式获取下一个元素、多一步fail-fast验证
public T next() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
return nextElement();
}
/**
* 仅用于Iterator方式中的删除当前元素、通过计算最后一个返回的元素的hash值
* 定位到table中对应的元素、删除。
*/
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) {
Entry[] tab = Hashtable.this.table;
int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;
for (Entry e = tab[index], 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();
}
}
}
b)使用Enumeration迭代时、获取Enumeration过程
(1)通过keys枚举:
(2)通过elements枚举
c)使用Iterator迭代
(1)通过keySet()获取Set
(2) 通过values()获取Collection
(3)通过entrySet()获取Set
4、rehash():当使用Hashtable对外提供的put()方法时、put()方法内部会检测容量是否大于等于阀值、是的话调用rehash()、重构。
/** 调整Hashtable的长度,将长度变成原来的(2倍+1)
* 1、使用临时变量记录原来table中值
* 2、创建一个新的容量为原来table容量*2 + 1 的table
* 3、将原来table中值重新赋给新table
*/
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
int newCapacity = oldCapacity * 2 + 1;
Entry[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int)(newCapacity * loadFactor);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry old = oldMap[i] ; old != null ; ) {
Entry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}
/** 将键值对存入Hashtable、不允许value为null*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// 如果存在相同key、则使用传入value替代旧的的value
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)) {
V old = e.value;
e.value = value;
return old;
}
}
// 若“Hashtable中不存在键为key的键值对”
//修改Hashtable结构变动次数
modCount++;
//如果Hashtable中键值对总数大于等于阀值、则rehash()、即将容量扩增2倍+1
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
}
// 将“Hashtable中index”位置的Entry(链表)保存到e中
Entry e = tab[index];
//创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置e为新的Entry的下一个元素。
tab[index] = new Entry(hash, key, value, e);
//容量+1
count++;
return null;
}
1、遍历方式:
a)通过keys获取枚举类型对象遍历:
Enumeration e = hashtable.keys();
b)通过elements获取枚举类型对象对象遍历:
Enumeration e = hashtable.elements();
c)通过keySet获取Set类型对象的Iterator遍历:
Set keySet = hashtable.keySet();
Iterator it = keySet.iterator();
d)通过values获取Collection类型对象的Iterator遍历:
Collection values = hashtable.values();
Iterator it = values.iterator();
e)通过entrySet获取Set类型对象的Iterator遍历:
Set> entrySet = hashtable.entrySet();
Iterator> it = entrySet.iterator();
2、迭代示例:
package com.chy.collection.example;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
public class EragodicHashtable {
//初始化Hashtable
private static Hashtable hashtable = new Hashtable();
static{
for (int i = 0; i < 10; i++) {
hashtable.put(""+i, ""+i);
}
}
/**
* 测试使用keys获取Enumeration遍历
*/
private static void testKeys(){
Enumeration e = hashtable.keys();
while(e.hasMoreElements()){
System.out.println("hash table k :" + e.nextElement());
}
System.out.println("==============================================");
}
/**
* 测试使用elements获取Enumeration遍历
*/
private static void testElements(){
Enumeration e = hashtable.elements();
while(e.hasMoreElements()){
System.out.println("hash table vlaue :" + e.nextElement());
}
System.out.println("==============================================");
}
/**
* 测试使用keySet获取Set的Iterator遍历
*/
private static void testKeySet(){
Set keySet = hashtable.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()){
System.out.println("hash table k : " + it.next());
}
System.out.println("==============================================");
}
/**
* 测试使用values获取Collection的Iterator遍历
*/
private static void testValues(){
Collection values = hashtable.values();
Iterator it = values.iterator();
while(it.hasNext()){
System.out.println("hash table value : " + it.next());
}
System.out.println("==============================================");
}
/**
* 测试使用entrySet>的Iterator遍历
*/
private static void testEntrySet(){
Set> entrySet = hashtable.entrySet();
Iterator> it = entrySet.iterator();
while(it.hasNext()){
System.out.println("hash table entry : " + it.next());
}
System.out.println("==============================================");
}
public static void main(String[] args) {
testKeys();
testElements();
testKeySet();
testValues();
testEntrySet();
}
}
3、API示例:
package com.chy.collection.example;
import java.util.HashMap;
import java.util.Hashtable;
@SuppressWarnings("all")
public class HashtableTest {
/**
* 测试构造方法、下面四个方法效果相同、
*/
private static void testConstructor(){
//use default construct
Hashtable ht1 = new Hashtable();
//use specified initCapacity
Hashtable ht2 = new Hashtable(11);
//use specified initCapacity and loadFactor
Hashtable ht3 = new Hashtable(11, 0.75f);
//use specified Map
Hashtable ht4 = new Hashtable(ht1);
}
/**
* 测试API方法
*/
public static void main(String[] args) {
//初始化、键-值都为字符串"1"的hashMap
Hashtable ht = new Hashtable();
for (int i = 0; i < 10; i++) {
ht.put(""+i, ""+i);
}
/*
* 向Hashtable中添加键为null的键值对
* 只会在Hashtable的index为0处、保存一个键为null的键值对、键为null、值为最后一次添加的键值对的值。
*/
ht.put(null, null);
ht.put(null, "n");
System.out.println(ht.size());
System.out.println(ht);
//是否包含键“1”
System.out.println("Hashtable contans key ? " + ht.containsKey("1"));
//是否包含值“1”
System.out.println("Hashtable contans value ? " + ht.containsValue("1"));
//获取键为“1”的值
System.out.println("the value of key=1 " + ht.get("1"));
//将键为“1”的值修改成"11"
ht.put("1", "11");
//将Hashtable复制到Hashtable1中
Hashtable Hashtable1 = (Hashtable)ht.clone();
//将Hashtable1所有键值对复制到Hashtable中
ht.putAll(Hashtable1);
System.out.println(ht);//不会有二十个元素、因为他不会再添加重复元素
//如果Hashtable非空、则清空
if(!ht.isEmpty()){
ht.clear();
}
System.out.println(ht.size());
}
}