java_集合体系之Hashtable详解、源码及示例——10

java_集合体系之Hashtable详解、源码及示例——10

 

一:Hashtable结构图

 

 

 

简单说明:

        1、上图中虚线且无依赖字样、说明是直接实现的接口

        2、虚线但是有依赖字样、说明此类依赖与接口、但不是直接实现接口

        3、实线是继承关系、类继承类、接口继承接口

        4、实现Serielazable接口、允许使用ObjectInputStream/ObjectOutputStream读取/写入

        5、实现Map接口、以键值对形式存储数据

        6、实现Cloneable接口、允许克隆Hashtable

        7、继承Dictionary、说明Hashtable是键值对形式类、并且键、值都不允许为null。

        8、Dictionary依赖Enumeration、Hashtable可以使用Enumeration、Iterator迭代其中元素。


二:Hashtable类简介:

        1、  基于哈希表的Map结构的实现

        2、线程安全

        3、内部映射无序

        4、不允许值为null的key和value

 

三:Hashtable API


        1、构造方法

 

// 默认构造函数。
public Hashtable() 

// 指定“容量大小”的构造函数
public Hashtable(int initialCapacity) 

// 指定“容量大小”和“加载因子”的构造函数
public Hashtable(int initialCapacity, float loadFactor) 

// 包含“子Map”的构造函数
public Hashtable(Map 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 map)
synchronized V                   remove(Object key)
synchronized int                 size()
synchronized String              toString()
synchronized Collection       values()


四:Hashtable 源码分析


说明:

        1、对哈希表要有简单的认识、

        2、Hashtable是通过“拉链法”解决哈希冲突的

        3、理解Hashtable源码中的关键部分、Entry实体类的行为、属性。Entry的存储方式、Hashtable的扩容方式、Hashtable内部关于获取新的hash code的算法。

        4、与遍历相关:可以使用Enumeration、也可以使用Iterator。

        5、与容量有关的内容Hashtable的实例有两个参数影响其性能:初始容量加载因子容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

        6、默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数Hashtable 类的操作中,包括getput 操作,都反映了这一点)。初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于Hashtable 所包含的最大条目数除以加载因子,则永远不会发生 rehash 操作。但是,将初始容量设置太高可能会浪费空间。

        7、如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

        8、如果很多映射关系要存储在 Hashtable 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

 

总结 :

        1、数据结构:Hashtable是以哈希表的形式存储数据的、并且是通过“拉链法”解决冲突、Hashtable中存储的Entry继承Map.Entry即实现了getKey() getValue() setValue() equals() hashCode()方法、关于Hashtable存储元素的结构

 	/** 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的Iterator


                           



                      (2) 通过values()获取Collection的Iterator


                           



                       (3)通过entrySet()获取Set>的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 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;
    }


五:Hashtable 示例


        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());
	}
}


更多内容:java_集合体系之总体目录——00



你可能感兴趣的:(java_集合体系之Hashtable详解、源码及示例——10)