HashTable原理与源码分析

本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作,如有错误之处忘不吝批评指正!

HashTable内部存储结构

 HashTable内部存储结构为数组+单向链表的形式存储数据,即定义的 Entry<?,?>[] table 变量

   


源码分析

变量定义

   //使用Entry数组存储数据 (Entry 单向链表)
    private transient Entry<?,?>[] table;
    //已经存储在table 的 Entry 个数
    private transient int count;
    /****
    * Entry数组扩容阈值(count)  count>=threshold 时扩容 Entry数组会进行扩容 
    * 建议不要设置超过1 更要不设置太大,导致链表长度过长 会导致查询很慢
     * 比如 HashTble initialCapacity =5  loadFactor=0.75 则计算 threshold =3
     * when count >=3 时 table开始扩容(具体如何扩容的看扩容代码)
     * ***/ 
    private int threshold;
    //负载因子 用于计算 threshold  
    private float loadFactor;
    //记录修改次数
    private transient int modCount = 0;
    //table数组的最大长度
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

Entry点向链表结构

/**
 * Hashtable bucket collision list entry
 *单向链表
 */
private static class Entry<K,V> implements Map.Entry<K,V> {
    //hash值
    final int hash;
    //key
    final K key;
    //value
    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;
    }

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

    // Map.Entry Ops
    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==null ? e.getKey()==null : key.equals(e.getKey())) &&
           (value==null ? e.getValue()==null : value.equals(e.getValue()));
    }

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

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

构造函数

  /***
      * 初始化HashTable 指定初始化容量(initialCapacity),负载因子(loadFactor)
      * 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;
        //初始化Entry数组
        table = new Entry<?,?>[initialCapacity];
        //计算扩容阈值
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

   /***
    * 初始化HashTable 指定初始化容量(initialCapacity)
    * 默认负载因子大小为0.75
    * ***/
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

    /***
      * 默认初始化 Entry数组 大小为 11
      * loadFactor =0.75
      ***/    
    public Hashtable() {
        this(11, 0.75f);
    }

    /***
     * 
     * 
     ***/
    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

put方法

/***
 **添加元素   key value
 ** 注意:put方法是加锁的 这就是 hashTable 为啥是线程安全的原因 阻塞的
** key、value 均不能为空 ***
*/ public synchronized V put(K key, V value) { // Make sure the value is not null value不能为空 if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; //hashCode取的是key的HashCode key不能为空 int hash = key.hashCode(); //根据hashCode & long最大值散列 再对 数组长度取模获取到所要插入的数组下标 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") //查看数组该下标下是否存在元素 如存在便利 value(新)覆盖key相同的值 Entry<K,V> entry = (Entry<K,V>)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; } } //保存 addEntry(hash, key, value, index); return null; } /*** *保存 ***/ private void addEntry(int hash, K key, V value, int index) { //记录put次数 +1 modCount++; Entry<?,?> tab[] = table; //已存储的Entry个数 >= 阈值 扩容 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. @SuppressWarnings("unchecked") //查询数组原有的Entry链表 Entry<K,V> e = (Entry<K,V>) tab[index]; //将Entry<K,V> 保存 并将Entry<K,V>.next 指向原来的Entry链表 tab[index] = new Entry<>(hash, key, value, e); //数组长度+1 count++; }

扩容机制

/****
  **扩容
  ** 触发  count(已存储的Entry个数) >= threshold(阈值)
  ****/
   protected void rehash() {
    //扩容前 数组(table)长度   
    int oldCapacity = table.length;
    //扩容前的 table数组
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    //扩容后的 数组长度为 oldCapacity*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;
    }
    // 新建一个数组长度为 oldCapacity*2+1 数组
    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<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;
        }
    }
}

 特点:

    1. key、value 都不允许为空 key不允许为空 因为hashcode取的是key 该对象的HashCode()
    2. HashTable 默认构造方法 容量初始化为 11 负载因子为 0.75   若使用带负载因子的构造方法创建HashTable 请不要讲负载因子设置过大 比如 初始化容量设为 1 负载因子设为1000 这样会导致查询很慢
    3. HashTable是数组+单项链表Entry<k,v>的结构来存储数据
    4. 数组table最大长度为 Integer.Max - 8;
    5. 扩容条件 count(HashTbale 存储链表的个数) >= threshold(阈值 计算方法 table.length * loadFactor)
    6. 线程安全
posted @ 2019-01-16 20:58  远方的风lyh  阅读(760)  评论(2编辑  收藏  举报