Java集合框架 —— Hashtable

 2023-02-01
原文作者:蒋先森 原文地址:https://jlj98.top/

声明:本文使用JDK1.8,如果有错希望指出

在Java面试的时候,HashMap 和 Hashtable 经常被问,就想仔细分析下两者。

Java集合框架 —— HashMap

HashMap 和 Hashtable 的区别

HashMap 和 Hashtable 都是实现 Map 接口,两者主要的区别是线程安全性,同步,以及速度。

HashMap Hashtable
键值接受null 键值对不能为null
非synchronized Hashtable是synchronized,线程安全
单线程下HashMap的速度比Hashtable快 sychronized意味着在一次仅有一个线程能够更改Hashtable
迭代器(Iterator)是fail-fast迭代器 Hashtable的enumerator迭代器不是fail-fast的

因此仅在需要线程安全的时候使用Hashtable,而如果Java5以上,还是使用 ConcurrentHashMap。

Java并发容器 ——— ConcurrentHashMap

源码分析

202212301148024631.png

Hashtable 虽然不是继承于 AbstractMap,但它继承于 Dictionary(Dictionary 也是键值对的接口),而且也实现Map接口;因此,Hashtable 的内容也是“键值对,也不保证次序”。但和 HashMap 相比,Hashtable 是线程安全的,而且它支持通过 Enumeration 去遍历。Hashtable 采用的是 数组+链表 实现。Hashtable 通过 synchronized 保证其的线程安全。

数据结构

    public class Hashtable<K,V>
        extends Dictionary<K,V>
        implements Map<K,V>, Cloneable, java.io.Serializable {
        //
        private transient Entry<?,?>[] table;
        //元素个数
        private transient int count;
        //table数组扩容的节点数阈值,默认情况下为 8
        private int threshold;
        //负载因子,默认 0.75
        private float loadFactor;
        //
        private transient int modCount = 0;
        
        private static class Entry<K,V> implements Map.Entry<K,V> {
            final int hash;
            final K key;
            V value;
            Entry<K,V> next;
    
            protected Entry(int hash, K key, V value, Entry<K,V> next) {
                this.hash = hash;
                this.key =  key;
                this.value = value;
                this.next = next;
            }
        }

初始化构造

    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];
        //比较 initialCapacity * loadFactor和MAX_ARRAY_SIZE + 1,去两者的最小值,默认情况下 扩容阈值为 8
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }
    //默认负载因子 0.75
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
    //默认容量11,负载因子0.75
    public Hashtable() {
        this(11, 0.75f);
    }

put() 操作

    public synchronized V put(K key, V value) {
        // 限制 value不能为null
        if (value == null) {
            throw new NullPointerException();
        }
    
        Entry<?,?> tab[] = table;
        //如果key为null会报错,key也不能为null
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //根据key的hash值寻址
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            //如果已存在,替换旧的值,并返回旧的value
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        //通过addEntry将新节点添加进链表
        addEntry(hash, key, value, index);
        return null;
    }
    
    private void addEntry(int hash, K key, V value, int index) {
        modCount++;
    
        Entry<?,?> tab[] = table;
        //如果元素个数超过阈值threshold,进行扩容
        if (count >= threshold) {
            rehash();
    
            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }
        //把新的节点房贷table[index]位置
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

扩容 rehash()

    protected void rehash() {
        int oldCapacity = table.length;//获取旧容器的长度
        Entry<?,?>[] oldMap = table;//复制旧的map
    
        //oldCapacity << 1 向左移一位,相当于乘以2,最后还加上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;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
    
        modCount++;
        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;
            }
        }
    }

get() 操作

    public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }