Java提高至Hashtable

基本环境


Mac OS X EI Capitan 版本 10.11.6

AndroidStudio 2.3.2

Java API为Android API 25 Platform下的 Java API

源码基于 /Users/du/Library/Android/sdk/platforms/android-25/android.jar!/java/util/Hashtable.class


Hashtable的实现原理


● HashMap基于hashing的原理,进行存储 HashtableEntry[由每个键值对构造],使用拉链法解决哈希地址冲突。
● 使用put(key, value)方法来存储键值对,先计算出key的hashCode值,然后构造出HashtableEntry,根据hashCode值在哈希表中的索引决定每个HashtableEntry的存储位置。
● 使用get(key)来获取value,先计算出key的hashCode值,然后根据hashCode值在哈希表中的索引循环遍历链表查找value。


Hashtable的特点


● 基于数组实现,数组里的元素是一个单向链表[参考哈希表解决冲突方法之拉链法]
● key不可重复,value可重复,key、value不能为null
● 线程安全


Hashtable的成员变量


    //哈希表
    private transient HashtableEntry<K,V>[] table;

    //哈希表中HashtableEntry的个数,即关键字的集合大小,非哈希表长度
    private transient int count;

    //当哈希表的大小超过超过阈值的时候要进行扩容,阈值 = capacity * loadFactor
    private int threshold;

    //装填因子
    private float loadFactor;

    //Hashtable的改变次数,用来实现“fail-fast”机制。
    //在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,
    //并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你
    private transient int modCount = 0;


Hashtable的内部类


        //HashtableEntry代表了“拉链”的节点,每一个HashtableEntry代表了一个键值对,
        //哈希表的"key-value键值对"都是存储在HashtableEntry数组中的。
        static class HashtableEntry<K,V> implements Map.Entry<K,V> {
            int hash;
            final K key;
            V value;
            HashtableEntry<K,V> next;//next使得每个哈希地址相同的同义词形成链表。

            protected HashtableEntry(int hash, K key, V value, HashtableEntry<K,V> next) {
                this.hash = hash;
                this.key =  key;
                this.value = value;
                this.next = next;
            }

            protected Object clone() {
                return new HashtableEntry<>(hash, key, value,
                                      (next==null ? null : (HashtableEntry<K,V>) next.clone()));
            }

            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.equals(e.getKey()) && value.equals(e.getValue());
            }

            public int hashCode() {
                return (Objects.hashCode(key) ^ Objects.hashCode(value));
            }

            public String toString() {
                return key.toString()+"="+value.toString();
            }
        }


Hashtable的构造方法


    //提供初始容量和装填因子进行构造
    //不同于HashMap,这里的initialCapacity是哈希表真实的表长,HashMap是2^initialCapacity
    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 HashtableEntry[initialCapacity];
        threshold = (initialCapacity <= MAX_ARRAY_SIZE + 1) ? initialCapacity : MAX_ARRAY_SIZE + 1;
    }

    //提供初始容量进行构造
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    //空构造方法,哈希表长为11,装填因子为0.75
    public Hashtable() {
        this(11, 0.75f);
    }

    //提供一个Map进行构造
    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }


Hashtable的主要操作方法


1. put方法

    public class Hashtable<K,V>
        extends Dictionary<K,V>
        implements Map<K,V>, Cloneable, java.io.Serializable {//可克隆,可序列化

        //同步,保证线程安全
        public synchronized V put(K key, V value) {
            //value不能为空,会报NullPointerException
            if (value == null) {
                throw new NullPointerException();
            }

            HashtableEntry tab[] = table;
            //根据key计算hash值
            int hash = hash(key);
            //根据hash值和哈希表长度[即数组容量],找到索引值
            //hash & 0x7FFFFFFF 操作,保证为正数
            int index = (hash & 0x7FFFFFFF) % tab.length;
            //遍历table[i]位置的链表,若有相同的key,则用新值覆盖旧值
            for (HashtableEntry<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) {
                //如果达到了扩容阈值,则进行扩容,容量是原来的 2 * oldCapacity + 1
                rehash();

                tab = table;
                hash = hash(key);
                index = (hash & 0x7FFFFFFF) % tab.length;
            }

            //构造新的HashtableEntry结点,添加到表头
            HashtableEntry<K,V> e = tab[index];
            tab[index] = new HashtableEntry<>(hash, key, value, e);
            count++;
            return null;
        }

        //如果达到了扩容阈值,则进行扩容,容量是原来的 2 * oldCapacity + 1
        protected void rehash() {
            int oldCapacity = table.length;
            HashtableEntry<K,V>[] oldMap = table;

            //容量是原来的 2 * oldCapacity + 1
            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;
            }

            //新建一个长度为newCapacity的哈希表
            HashtableEntry<K,V>[] newMap = new HashtableEntry[newCapacity];

            modCount++;
            //重新计算阀值
            threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

            table = newMap;

            //将oldMap中的元素拷贝到newMap中
            for (int i = oldCapacity ; i-- > 0 ;) {
                for (HashtableEntry<K,V> old = oldMap[i] ; old != null ; ) {
                    HashtableEntry<K,V> e = old;
                    old = old.next;

                    int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                    e.next = newMap[index];
                    newMap[index] = e;
                }
            }
        }

    }

2. get方法

    public class Hashtable<K,V>
        extends Dictionary<K,V>
        implements Map<K,V>, Cloneable, java.io.Serializable {//可克隆,可序列化

        //synchronized同步方法,保证线程安全
        public synchronized V get(Object key) {
            HashtableEntry tab[] = table;
            //根据key计算hash值
            int hash = hash(key);
            //根据hash值和哈希表长度[即数组容量],找到索引值
            int index = (hash & 0x7FFFFFFF) % tab.length;
            //根据索引查找它所在的链表,并在单向链表中查找该元素
            for (HashtableEntry<K,V> e = tab[index] ; e != null ; e = e.next) {
                //hash值相等,且key相等时返回HashtableEntry的value值
                if ((e.hash == hash) && e.key.equals(key)) {
                    return e.value;
                }
            }
            return null;
        }

    }

3. remove方法

        public class Hashtable<K,V>
            extends Dictionary<K,V>
            implements Map<K,V>, Cloneable, java.io.Serializable {//可克隆,可序列化
    
            //synchronized同步方法,保证线程安全
            public synchronized V remove(Object key) {
                HashtableEntry tab[] = table;
                //根据key计算hash值
                int hash = hash(key);
                //根据hash值和哈希表长度[即数组容量],找到索引值
                int index = (hash & 0x7FFFFFFF) % tab.length;
                //根据索引查找它所在的链表,并在单向链表中查找该元素
                for (HashtableEntry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
                    //hash值相等,且key相等时删除该元素[实质上将该元素的后继元素e.next 链接到 该元素的前继元素prev.next]
                    if ((e.hash == hash) && e.key.equals(key)) {
                        modCount++;
                        if (prev != null) {
                            prev.next = e.next;
                        } else {
                            tab[index] = e.next;
                        }
                        count--;
                        V oldValue = e.value;
                        e.value = null;
                        return oldValue;
                    }
                }
                return null;
            }

      }


Hashtable的注意点


● key 和 value 均不可为 null 。

● Hashtable的方法是同步的,所以线程安全。

● Hashtable的默认初始大小是11,容量尽量使用素数、奇数,因为简单的取模,哈希的结果会更加均匀。

● 在Hashtable中有这么一段注释:

      If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable.
      If a thread-safe highly-concurrent implementation is desired, then it is recommended to use java.util.concurrent.ConcurrentHashMap in place of Hashtable.    

    如果不需要实现线程安全,推荐使用HashMap替代Hashtable;如果需要实现高并发下的线程安全,推荐使用ConcurrentHashMap代替Hashtable。

    以上的意思呢,就是Hashtable已经不推荐使用了。

● 每一版本的JDK,都会对HashMap和Hashtable的内部实现做优化。因为有老的代码还在使用它,所以优化之后,这些老的代码也能获得性能提升。




参考链接

HashMap和HashTable到底哪不同?

Java集合(五)- HashTable详解

java提高篇(二五)-----HashTable



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值