简单说明:
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<V> elements() synchronized Set<Entry<K, V>> entrySet() synchronized boolean equals(Object object) synchronized V get(Object key) synchronized int hashCode() synchronized boolean isEmpty() synchronized Set<K> keySet() synchronized Enumeration<K> 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<V> 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<K,V>即实现了getKey() getValue() setValue() equals() hashCode()方法、关于Hashtable存储元素的结构
/** Hashtable中表示节点的实体类、本质是一个单向链表*/ private static class Entry<K,V> implements Map.Entry<K,V> { int hash; 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; } protected Object clone() { return new Entry<K,V>(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 ^ (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<T> implements Enumeration<T>, Iterator<T> { //指向当前Hashtable的table Entry[] table = Hashtable.this.table; int index = table.length; Entry<K,V> entry = null; Entry<K,V> 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<K,V> 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<K,V> 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<K,V> 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<K,V> 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<K>的Iterator
(2) 通过values()获取Collection<K>的Iterator
(3)通过entrySet()获取Set<Map.Entry<K,V>>的Iterator
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<K,V> old = oldMap[i] ; old != null ; ) { Entry<K,V> 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<K,V> 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<K,V> e = tab[index];
//创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置e为新的Entry的下一个元素。
tab[index] = new Entry<K,V>(hash, key, value, e);
//容量+1
count++;
return null;
}
1、遍历方式:
a)通过keys获取枚举类型对象遍历:
Enumeration<String> e = hashtable.keys();
b)通过elements获取枚举类型对象对象遍历:
Enumeration<String> e = hashtable.elements();
c)通过keySet获取Set类型对象的Iterator遍历:
Set<String> keySet = hashtable.keySet(); Iterator<String> it = keySet.iterator();
d)通过values获取Collection类型对象的Iterator遍历:
Collection<String> values = hashtable.values(); Iterator<String> it = values.iterator();
e)通过entrySet获取Set类型对象的Iterator遍历:
Set<Entry<String, String>> entrySet = hashtable.entrySet(); Iterator<Entry<String, String>> 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<String, String> hashtable = new Hashtable<String, String>(); static{ for (int i = 0; i < 10; i++) { hashtable.put(""+i, ""+i); } } /** * 测试使用keys获取Enumeration遍历 */ private static void testKeys(){ Enumeration<String> e = hashtable.keys(); while(e.hasMoreElements()){ System.out.println("hash table k :" + e.nextElement()); } System.out.println("=============================================="); } /** * 测试使用elements获取Enumeration遍历 */ private static void testElements(){ Enumeration<String> e = hashtable.elements(); while(e.hasMoreElements()){ System.out.println("hash table vlaue :" + e.nextElement()); } System.out.println("=============================================="); } /** * 测试使用keySet获取Set<K>的Iterator遍历 */ private static void testKeySet(){ Set<String> keySet = hashtable.keySet(); Iterator<String> it = keySet.iterator(); while(it.hasNext()){ System.out.println("hash table k : " + it.next()); } System.out.println("=============================================="); } /** * 测试使用values获取Collection<V>的Iterator遍历 */ private static void testValues(){ Collection<String> values = hashtable.values(); Iterator<String> it = values.iterator(); while(it.hasNext()){ System.out.println("hash table value : " + it.next()); } System.out.println("=============================================="); } /** * 测试使用entrySet<Map.Entry<K, V>>的Iterator遍历 */ private static void testEntrySet(){ Set<Entry<String, String>> entrySet = hashtable.entrySet(); Iterator<Entry<String, String>> 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<String, String> ht1 = new Hashtable<String, String>(); //use specified initCapacity Hashtable<String, String> ht2 = new Hashtable<String, String>(11); //use specified initCapacity and loadFactor Hashtable<String, String> ht3 = new Hashtable<String, String>(11, 0.75f); //use specified Map Hashtable<String, String> ht4 = new Hashtable<String, String>(ht1); } /** * 测试API方法 */ public static void main(String[] args) { //初始化、键-值都为字符串"1"的hashMap Hashtable<String, String> ht = new Hashtable<String, String>(); 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<String, String> Hashtable1 = (Hashtable<String, String>)ht.clone(); //将Hashtable1所有键值对复制到Hashtable中 ht.putAll(Hashtable1); System.out.println(ht);//不会有二十个元素、因为他不会再添加重复元素 //如果Hashtable非空、则清空 if(!ht.isEmpty()){ ht.clear(); } System.out.println(ht.size()); } }