zoukankan      html  css  js  c++  java
  • HashTable的实现原理

    一、---使用方式---

    (1)Hashtable 是一个散列表,它存储的内容是键值对(key-value)映射。

    (2)Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

    (3)Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。

    如下是Hashtable 的简单使用方式:在遍历时使用是三种遍历方式来对其进行遍历

    package ThreeWeek;  
      
    import java.util.Enumeration;  
    import java.util.Hashtable;  
    import java.util.Iterator;  
    import java.util.Map;  
    import java.util.Map.Entry;  
      
    public class HashTableTest {  
      
        public static void main(String args[]){  
            Hashtable<String, Integer> table = new Hashtable<String, Integer>();  
              
            //[1]添加元素  
            table.put("zhangsan", 22);  
            table.put("lisi", 33);  
            table.put("wangwu", 44);  
              
            //[2]toString()方式打印  
            System.out.println(table.toString());  
              
            //[3]Iterator遍历方式1--键值对遍历entrySet()  
            Iterator<Entry<String, Integer>> iter = table.entrySet().iterator();  
            while(iter.hasNext()){  
                Map.Entry<String, Integer> entry = (Map.Entry<String, Integer>)iter.next();  
                String key = entry.getKey();  
                int value = entry.getValue();  
                System.out.println("entrySet:"+key+" "+value);  
            }  
              
            System.out.println("====================================");  
              
            //[4]Iterator遍历方式2--key键的遍历  
            Iterator<String> iterator = table.keySet().iterator();  
            while(iterator.hasNext()){  
                String key = (String)iterator.next();  
                int value = table.get(key);  
                System.out.println("keySet:"+key+" "+value);  
            }  
              
            System.out.println("====================================");  
              
            //[5]通过Enumeration来遍历Hashtable  
            Enumeration<String> enu = table.keys();  
            while(enu.hasMoreElements()) {  
                System.out.println("Enumeration:"+table.keys()+" "+enu.nextElement());  
            }   
                  
        }  
    }  
    

      

    输出:

    {zhangsan=22, lisi=33, wangwu=44}  
    entrySet:zhangsan 22  
    entrySet:lisi 33  
    entrySet:wangwu 44  
    ====================================  
    keySet:zhangsan 22  
    keySet:lisi 33  
    keySet:wangwu 44  
    ====================================  
    Enumeration:java.util.Hashtable$Enumerator@139a55 zhangsan  
    Enumeration:java.util.Hashtable$Enumerator@1db9742 lisi  
    Enumeration:java.util.Hashtable$Enumerator@106d69c wangwu  
    

      

    二、---内部原理---

    1、继承关系

    java.lang.Object  
       ↳     java.util.Dictionary<K, V>  
             ↳     java.util.Hashtable<K, V>  
      
    public class Hashtable<K,V> extends Dictionary<K,V>  
        implements Map<K,V>, Cloneable, java.io.Serializable { }  

    与HashMap不同的是Hashtable是继承Dictionary,实现了Map接口。Map是"key-value键值对"接口,Dictionary是声明了操作"键值对"函数接口的抽象类。 

    2、构造函数

    (1)Hashtable中提供了四个构造函数,如下:

    // 默认构造函数。  
    public Hashtable()   
      
    // 指定“容量大小”的构造函数  
    public Hashtable(int initialCapacity)   
      
    // 指定“容量大小”和“加载因子”的构造函数  
    public Hashtable(int initialCapacity, float loadFactor)   
      
    // 包含“子Map”的构造函数  
    public Hashtable(Map<? extends K, ? extends V> t)  

    (2)上面的四个构造方法中,第三个是最重要的,指定初始化容量和构造因子

    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,获得大小为initialCapacity的table数组    
            table = new Entry[initialCapacity];    
            //计算阀值    
            threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);    
            //初始化HashSeed值    
            initHashSeedAsNeeded(initialCapacity);    
        }    

    3、成员变量

    (1)table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。 

    (2)count是Hashtable的大小,它是Hashtable保存的键值对的数量。 

    (3)threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。

    (4)loadFactor就是加载因子。

    (5)modCount是用来实现fail-fast机制的

    private transient Entry[] table;  
    // Hashtable中元素的实际数量  
    private transient int count;  
    // 阈值,用于判断是否需要调整Hashtable的容量(threshold = 容量*加载因子)  
    private int threshold;  
    // 加载因子  
    private float loadFactor;  
    // Hashtable被改变的次数  
    private transient int modCount = 0;  

    4、put和get方法

    (1)put方法

    从下面的代码中我们可以看出,Hashtable中的key和value是不允许为空的,当我们想要想Hashtable中添加元素的时候,首先计算key的hash值,然

    后通过hash值确定在table数组中的索引位置,最后将value值替换或者插入新的元素,如果容器的数量达到阈值,就会进行扩充。

    public synchronized V put(K key, V value) {    
            // 确保value不为null    
            if (value == null) {    
                throw new NullPointerException();    
            }    
        
            /*  
             * 确保key在table[]是不重复的  
             * 处理过程:  
             * 1、计算key的hash值,确认在table[]中的索引位置  
             * 2、迭代index索引位置,如果该位置处的链表中存在一个一样的key,则替换其value,返回旧值  
             */    
            Entry tab[] = table;    
            int hash = hash(key);    //计算key的hash值    
            int index = (hash & 0x7FFFFFFF) % tab.length;     //确认该key的索引位置    
            //迭代,寻找该key,替换    
            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;    
                }    
            }    
        
            modCount++;    
            if (count >= threshold) {  //如果容器中的元素数量已经达到阀值,则进行扩容操作    
                rehash();    
                tab = table;    
                hash = hash(key);    
                index = (hash & 0x7FFFFFFF) % tab.length;    
            }    
        
            // 在索引位置处插入一个新的节点    
            Entry<K,V> e = tab[index];    
            tab[index] = new Entry<>(hash, key, value, e);    
            //容器中元素+1    
            count++;    
            return null;    
        }    
    

     

    (2)get方法

    同样也是先获得索引值,然后进行遍历,最后返回

    public synchronized V get(Object key) {    
            Entry tab[] = table;    
            int hash = hash(key);    
            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)) {    
                    return e.value;    
                }    
            }    
            return null;    
        }    
    

    、---比较不同---

    Hashtable和HashMap到底有哪些不同呢

    (1)基类不同:HashTable基于Dictionary类,而HashMap是基于AbstractMap。Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。

    (2)null不同:HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。

    (3)线程安全:HashMap时单线程安全的,Hashtable是多线程安全的。

    (4)遍历不同:HashMap仅支持Iterator的遍历方式,Hashtable支持Iterator和Enumeration两种遍历方式。

  • 相关阅读:
    [BZOJ3510][洛谷P4299]首都(LCT)
    [luogu P5325][模板]Min_25筛
    [洛谷P3288][SCOI2014][BZOJ3597]方伯伯运椰子(网络流+图论)
    [洛谷P5342][TJOI2019]甲苯先生的线段树(数位dp)
    [CQOI2012][洛谷P3159][BZOJ2668]交换棋子(网络流+图论)
    [SDOI2015]约数个数和(莫比乌斯反演)
    使用Visual Studio 2005扩展创建Windows SharePoint Services 3.0 Web Part
    C#操作xml文件
    实用JS代码
    ASP.NET分页存储过程自定义用户控件(转)
  • 原文地址:https://www.cnblogs.com/owenma/p/8554568.html
Copyright © 2011-2022 走看看