深入学习java集合:Hashtable<K,V>实现

来源:互联网 发布:数据库获取当前时间 编辑:程序博客网 时间:2024/06/05 04:49
1、Hashtable类图

         从中可以看出HashTable继承Dictionary类,实现Map接口。其中Dictionary类是任何可将键映射到相应值的类(如 Hashtable)的抽象父类。每个键和每个值都是一个对象。在任何一个 Dictionary 对象中,每个键至多与一个值相关联。Map是"key-value键值对"接口。 
         Hashtable是一个古老的 Map 实现类,一般不建议使用 ,Hashtable是一个线程安全的 Map 实现Hashtable不允许使用 null 作为 key 和 value与 HashSet集合不能保证元素的顺序的顺序一样,Hashtable也不能保证其中 key-value 对的顺序。Hashtable 判断两个 Key 相等的标准是:两个 Key 通过 equals 方法返回 truehashCode值也相等。

2、HashTable构造实现以及重要方法
        HashTable采用"拉链法"实现哈希表,它定义了几个重要的参数:table、count、threshold、loadFactor、modCount。
     
          

   /**
     * The hash table data.
     */
    private transient Entry<K,V>[] table;

  2)构造器
     HashTable提供了4个不同的构造器
// 构造一个初始大小为initialCapacity,装载因子为loadFactor的空的HashTable
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];    //初始化table,获得大小为initialCapacity的table数组  
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); //计算阀值  
        initHashSeedAsNeeded(initialCapacity);   //初始化HashSeed值  
    }
// 构造一个初始大小为initialCapacity,装载因子为默认的0.75的空的HashTable
 public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
//默认的无参数构造器,默认的初始大小为16,装载因子为0.75
 public Hashtable() {
        this(11, 0.75f);
    }
3)  hash(Object k): int  就算hash值的函数
 private int hash(Object k) {
        // hashSeed will be zero if alternative hashing is disabled.
        return hashSeed ^ k.hashCode();
    }
4) put(key: K, value: V): V   添将指定 key 映射到此哈希表中的指定 value方法
        可以看到该方法加了同步处理 ,首先 计算key的hash值,根据hash值获得key在table数组中的索引位置,然后迭代该key处的Entry链表,若该链表中存在一个这个的key对象,那么就直接替换其value值即可,否则在将改key-value节点插入该index索引位置处。
      public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {   //确保不为空
            throw new NullPointerException();
        }
        // Makes sure the key is not already in the hashtable.
        Entry tab[] = table;
        int hash = hash(key);    //计算key的hash值
        int index = (hash & 0x7FFFFFFF) % tab.length;  //计算在table数组中的索引下标,也就是对应的桶
        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 the table if the threshold is exceeded
            rehash();
            tab = table;
            hash = hash(key);
            index = (hash & 0x7FFFFFFF) % tab.length;
        }
        // Creates the new entry.
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }
5) get(key: Object): V    获取HashTable中,key对应的value方法
 //  处理过程就是计算key的hash值,判断在table数组中的索引位置,然后迭代链表,匹配直到找到相对应key的value,若没有找到返回null。
  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;
    }
6 )rehash(): void   HashTable 再hash方法
       HashTable的扩容操作是在put方法中,如果需要向table[]中添加Entry元素,会首先进行容量校验,如果容量已经达到了阀值,HashTable就会进行扩容处理rehash()。

 protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;
        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;     //新容量=旧容量 * 2 + 1  
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<K,V>[] newMap = new Entry[newCapacity];  //构建新的大小等于newCapacity的HashTable
        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);  //重新计算阀值
        boolean rehash = initHashSeedAsNeeded(newCapacity);  //重新计算Hash种子
        table = newMap;
        for (int i = oldCapacity ; i-- > 0 ;) {      //依次将原来的元素拷贝到新的Hashtable中
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;
                if (rehash) {
                    e.hash = hash(e.key);
                }
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = newMap[index];
                newMap[index] = e;
            }
        }
    }

3、HashTable与HashMap区别
        HashTable和HashMap存在很多的相同点,但是他们还是有几个比较重要的不同点。

        第一:我们从他们的定义就可以看出他们的不同,HashTable基于Dictionary类,而HashMap是基于AbstractMap。Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作。

        第二:HashMap可以允许存在一个为null的key和任意个为null的value,但是HashTable中的key和value都不允许为null。当HashMap遇到为null的key时,它会调用putForNullKey方法来进行处理。对于value没有进行任何处理,只要是对象都可以。

        第三:Hashtable的方法是同步的,而HashMap的方法不是。所以有人一般都建议如果是涉及到多线程同步时采用HashTable,没有涉及就采用HashMap,但是在Collections类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的Map对象,并把它作为一个封装的对象来返回,所以通过Collections类的synchronizedMap方法是可以我们你同步访问潜在的HashMap。








0 0
原创粉丝点击