Hashtable(jdk1.8)源码分析

hashtable 的定义:

public class HashTable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable 
继承自Dictionary,Dictionary是一个抽象父类,功能和Map一样,但过时了,官方推荐用实现Map接口来取代。 
并且实现了Map接口,以及Cloneable,Serializable接口。


和hashMap的区别:

null值问题

HashTable键(key)和值(value)均不能为null。 


/**
     * 将key和value加入到map中,明显标明,
     * value不能为null。如果key为null,则会报nullPointer异常 */
    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 = key.hashCode();
        //很直接的利用hashcode去除table.length,然后取长度。
        int index = (hash & 0x7FFFFFFF) % tab.length;

        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        //链表后面有数据
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                //hash相同且equals,那么就连在后面,是用链表的方式。
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        //第一个,链表后面没有数据。
        addEntry(hash, key, value, index);
        return null;
    }
关于value,明显有if判断,不能为null, 

如果key为null,则也直接在计算hashCode的时候就会报空指针。


计算table数组索引值方法

        int hash = key.hashCode();     //直接用hashcode % n 
        int index = (hash & 0x7FFFFFFF) % tab.length;

而hashmap    int index=e.hash  % n ; 

 hash值的计算方式不同! 很直接的利用hashcode去除table.length,然后取长度。


HashTable是线程安全

因为HashTable中大部分方法都是加了synchronized关键字,所以同一时刻,只能有一个线程进入其方法故是线程安全的。


initialCapacity和loadFactor

HashMap中:initialCapacity=16,loadFactory=0.75 
HashTable中:initialCapacity=11,loadFactory=0.75


只有链表方式解决冲突

Java8,HashMap中,当出现冲突时,

  • 如果冲突数量小于8,则是以链表方式解决冲突。
  • 而当冲突大于等于8时,就会将冲突的Entry转换为红黑树进行存储。
  • 而又当数量小于6时,则又转化为链表存储。

而在HashTable中,  则都是以链表方式存储。


扩容的额度

Java8中, HashMap: 
一旦扩容,都是扩展到2的倍数,因为这样有利于计算数组索引值,即和计算数组索引结合起来。

HashTable:   一次性扩展为oldCapacity*2+1。 


/**
     * 一次扩展是,old*2+1
     */
    @SuppressWarnings("unchecked")
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 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<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        //新的threshold值。取newCapacity*loadFactor的小值。
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }
先扩展,再把旧数组里面元素一个一个加到新的里面。 

注意, 这里取hash不是e.hash,而仍然是key.hashCode计算保留下来的值。 


参考:

https://blog.csdn.net/anla_/article/details/78298484

https://blog.csdn.net/zldeng19840111/article/details/6703104

猜你喜欢

转载自blog.csdn.net/duoduo18up/article/details/80167074