Hashtable源码解析

转载:http://www.cnblogs.com/skywang12345/p/3310887.html

1、简介

和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。

Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable 的函数都是同步的,这意味着它是线程安全的它的key、value都不可以为null。此外,Hashtable中的映射不是有序的

Hashtable 的实例有两个参数影响其性能:初始容量加载因子

容量:是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。

加载因子:是对哈希表在其容量自动增加之前可以达到多满的一个尺度(阈值)。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。

通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。

2、关键点

  • Hashtable继承于Dictionary类,实现了Map接口。Map是"key-value键值对"接口,Dictionary是声明了操作"键值对"函数接口的抽象类。

  • Hashtable是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, count, threshold, loadFactor, modCount

    • table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。

    • count是Hashtable的大小,它是Hashtable保存的键值对的数量。

    • threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。

    • loadFactor就是加载因子。

    • modCount是用来实现fail-fast机制的。

3、Hashtable数据存储数组

    private transient Entry[] table;

Hashtable中的key-value都是存储在table数组中的。

4、数据节点Entry的数据结构

private static class Entry implements Map.Entry {
        final int hash;  //hash值
        final K key;     //键
        V value;         //值
        Entry next; //链表中下一个Entry的引用
        
        //Entry的构造函数
        protected Entry(int hash, K key, V value, Entry next) {
            this.hash = hash;
            this.key =  key;
            this.value = value;
            this.next = next;
        }

        @SuppressWarnings("unchecked")
        protected Object clone() {
            return new Entry<>(hash, key, value,
                                  (next==null ? null : (Entry) next.clone()));
        }

        // Map.Entry Ops

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        //设置value。若value是null,则抛出异常。
        public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        // 覆盖equals()方法,判断两个Entry是否相等。
        // 若两个Entry的key和value都相等,则认为它们相等。
        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;

            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }

        //Entry对象的hash值
        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

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

5、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];
        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);
    }

    // 包含“子Map”的构造函数
    public Hashtable(Map t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        // 将“子Map”的全部元素都添加到Hashtable中
        putAll(t);
    }

6、Hashtable的主要对外接口

6.1 clear()

clear() 的作用是清空Hashtable。它是将Hashtable的table数组的值全部设为null。

    public synchronized void clear() {
        Entry tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;  //将数组中的引用全部置为null
        count = 0;
    }

6.2 contains() 和 containsValue()

contains() 和 containsValue() 的作用都是判断Hashtable是否包含“值(value)”。

    public synchronized boolean contains(Object value) {
        // Hashtable中“键值对”的value不能是null,
        // 若是null的话,抛出异常!
        if (value == null) {
            throw new NullPointerException();
        }

        Entry tab[] = table;
        // 从后向前遍历table数组中的元素(Entry)
        // 对于每个Entry(单向链表),逐个遍历,判断节点的值是否等于value
        for (int i = tab.length ; i-- > 0 ;) {
            for (Entry e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }
    public boolean containsValue(Object value) {
        return contains(value);
    }

6.3 containsKey()

containsKey() 的作用是判断Hashtable是否包含key。

    public synchronized boolean containsKey(Object key) {
        Entry tab[] = table;
        int hash = key.hashCode();
        // 计算索引值,
        // % tab.length 的目的是防止数据越界
        int index = (hash & 0x7FFFFFFF) % tab.length;
        // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
        for (Entry e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return true;
            }
        }
        return false;
    }

6.4 elements()

elements() 的作用是返回“所有value”的枚举对象。

    public synchronized Enumeration elements() {
        return this.getEnumeration(VALUES);
    }
    // 获取Hashtable的枚举类对象
    private  Enumeration getEnumeration(int type) {
        if (count == 0) {
            return Collections.emptyEnumeration();
        } else {
            return new Enumerator<>(type, false);
        }
    }

7、put()和get()操作

7.1 put()操作

put() 的作用是对外提供接口,让Hashtable对象可以通过put()将“key-value”添加到Hashtable中。

    public synchronized V put(K key, V value) {
        // Hashtable中不能插入value为null的元素!!!
        if (value == null) {
            throw new NullPointerException();
        }

        // 若“Hashtable中已存在键为key的键值对”,
        // 则用“新的value”替换“旧的value”
        Entry tab[] = table;
        int hash = key.hashCode();
        //计算key的索引
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry entry = (Entry)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        //添加Entry
        addEntry(hash, key, value, index);
        return null;
    }
    private void addEntry(int hash, K key, V value, int index) {
        //将“修改统计数”+1
        modCount++;

        Entry tab[] = table;
        //若“Hashtable实际容量” > “阈值”(阈值=总的容量 * 加载因子)
        //则调整Hashtable的大小
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            //
            rehash();

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

        // Creates the new entry.
        //将“Hashtable中index”位置的Entry(链表)保存到e中
        @SuppressWarnings("unchecked")
        Entry e = (Entry) tab[index];
        //采用 “头插法”
        //创建“新的Entry节点”,并将“新的Entry”插入“Hashtable的index位置”,
        //并设置e为“新的Entry”的下一个元素(即“新Entry”为链表表头)。
        tab[index] = new Entry<>(hash, key, value, e);
        //将“Hashtable的实际容量”+1
        count++;
    }

7.2 get()操作

get() 的作用就是获取key对应的value,没有的话返回null。

    public synchronized V get(Object key) {
        Entry tab[] = table;
        //key的hash值
        int hash = key.hashCode();  
        //计算索引
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
        for (Entry e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

8、rehash()

rehash()函数负责hashtable的扩容。

    protected void rehash() {
        int oldCapacity = table.length;
        //扩容前的map
        Entry[] oldMap = table;

        // overflow-conscious code
        ////新容量 = 旧容量 * 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;

        //遍历旧的数组,将所有的键值对Entry移动到新数组中
        //注意,移动的是对象的引用
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry old = (Entry)oldMap[i] ; old != null ; ) {
                Entry e = old;
                old = old.next;
                
                //计算在新数组中的索引
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                //采用“头插法”
                e.next = (Entry)newMap[index];
                newMap[index] = e;
            }
        }
    }

9、remove()

remove() 的作用就是删除Hashtable中键为key的元素。

    public synchronized V remove(Object key) {
        Entry tab[] = table;
        int hash = key.hashCode();
        //计算索引
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry e = (Entry)tab[index];
        // 找到“key对应的Entry(链表)”
        // 然后在链表中找出要删除的节点,并删除该节点。
        for(Entry prev = null ; e != null ; prev = e, e = e.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;
                //将value引用置为null,回收value对象
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

你可能感兴趣的:(Hashtable源码解析)