源码阅读-HashTable

;HashMapHashTable的区别大家都知道,都继承了Map接口,可以存储<Key , Value>对,然后一个是线程安全的,另一个不是线程安全的。同样,现在即使是在多线程环境下也不推荐使用HashTable,推荐使用java.util.concurrent包中的ConcurrentHashMap,由于使用了分段锁,会得到更好的性能。

实现框架

HashTable的基本原理比较简单,在类中实现了Map接口和Map接口中的Entry接口。Entry接口的实现是一个链表,类的声明及成员变量如下:

private static class Entry<K,V> implements Map.Entry<K,V> {
        int hash;
        final K key;
        V value;
        Entry<K,V> next;
}

HashTable中比较重要的成员变量有

    /**
     * 存储数据的Entry数组
     */
    private transient Entry<K,V>[] table;

    /**
     * 在hash table的entry数量
     */
    private transient int count;

    /**
     * table重新hash的阈值,计算方法为(int)(capacity * loadFactor).)
     */
    private int threshold;

    /**
     * 上面公式用到的loadFactor
     */
    private float loadFactor;

HashTable的工作原理就是对一个传递进来的参数key求其hash值,对table的长度取余,得到index值,然后将对应key的value存到Entry中,如果在table[index]中已经有entry了,则会将newEntry用头插法插入到链表中。

        // Creates the new entry.
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);

Entry的构造函数:

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

发生rehash的情况

先把put函数的代码贴上来

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);
        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)) {
                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;
    }
  1. 可以看到当重复put一个key(hash相同,且equal)时,会将old值替换,并且返回old值,否则返回null。
  2. 不允许null的value出现
  3. 当count > threshold 时,会增大table的长度,并且重新hash。

rehash()的具体逻辑

protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        //about twice
        if (newCapacity - MAX_ARRAY_SIZE > 0) { //判断newCapacity是否溢出
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<K,V>[] newMap = new Entry[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        boolean rehash = initHashSeedAsNeeded(newCapacity);

        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            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;
            }
        }
    }

流程图为:

Created with Raphaël 2.1.0 start; 计算新的table大小(左移一位后加1); 是否溢出?; table.length=Integer.MAX_VALUE-8; 将所有的key放入新table中; end; yes no

最后,HashTable所有的方法都是synchronize方法,所以不管读取、写入,只能有一个线程在读取,性能较低。建议使用ConcurrentHashMap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值