Hashtable--JDK1.8源码分析

一、一些变量介绍

   //存储键值对的数组  
   private transient Entry<K,V>[] table;

   //键值对总数 
   private transient int count;  
  
   //容量的阈值,超过此容量将会扩容。    
   private int threshold;  
   
   //负载因子    
   private float loadFactor;  
   
   //hashtable被改变的次数,用于快速失败机制 
   private transient int modCount = 0;  

二、构造函数 

1.

    //无参默认构造函数,默认容量大小为11,负载比(负载因子)为0.75
    public Hashtable() {
        this(11, 0.75f);
    }

2.

    //支持自定义容量大小
    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

3.

    //支持自定义容量大小和负载比 
    public Hashtable(int initialCapacity, float loadFactor) {
        //容量不能小于0
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        //负载因子必须大于0
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);
        //如果容量传入的为0,则调整为1
        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        //初始化数组大小
        table = new Entry<?,?>[initialCapacity];
        //阈值在正常计算值和最大允许值之间取最小值
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

4. 

    //允许传入map结构的参数
    public Hashtable(Map<? extends K, ? extends V> t) {
        //容量取传入参数的两倍,如果值小于11则取11
        this(Math.max(2*t.size(), 11), 0.75f);
        //将数据存入到table中
        putAll(t);
    }
    
    //同步存入到table中,保证线程安全
    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

三、存储数据--put

    //同步存储数据
    public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {//如果存储的value为null,则抛出异常
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        //hash & 0x7FFFFFFF是为了保证得到的数是正数
        int index = (hash & 0x7FFFFFFF) % tab.length;//取模获取存储的下标位置
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        //如果有key值相同的数据,则更新为最新值,同时返回老的值
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        //如果没有key值相同的数据,则执行添加操作
        addEntry(hash, key, value, index);
        return null;
    }
    //添加元素
    private void addEntry(int hash, K key, V value, int index) {
        modCount++;//修改次数加1

        Entry<?,?> tab[] = table;
        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<K,V> e = (Entry<K,V>) tab[index];//取到原来该位置的元素
        tab[index] = new Entry<>(hash, key, value, e);//把最新的元素放到该数组位置,构造函数可把原来的元素e放到最新构建的元素后面,从而形成链表
        count++;
    }
    //元素构造函数 
    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()

    
    protected void rehash() {
        int oldCapacity = table.length;
        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++;//属于结构变化,修改次数+1;
        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;//当前要操作的元素e
                old = old.next;//为下次循环做准备

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];//获取该位置原来的元素,并将新元素的next属性指向他
                newMap[index] = e;//赋值为新元素
            }
        }
    }

五、get方法

public synchronized V get(Object key) {//根据键取出对应索引  
      Entry tab[] = table;  
      int hash = hash(key);//先根据key计算hash值  
      int index = (hash & 0x7FFFFFFF) % tab.length;//再根据hash值找到索引  
      for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {//遍历entry链  
          if ((e.hash == hash) && e.key.equals(key)) {//若找到该键  
              return e.value;//返回对应的值  
          }  
      }  
      return null;//否则返回null  
  } 

六、remove方法

public synchronized V remove(Object key) {//删除指定键值 
       Entry tab[] = table;  
       int hash = hash(key);//计算该键hash值  
       int index = (hash & 0x7FFFFFFF) % tab.length;//计算存储的下标位置  
       for (Entry<K,V> e = tab[index], 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;  
               e.value = null;  
               return oldValue;  
           }  
       }  
       return null;  
   } 

七、clear方法

    public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;//直接将每个数组置空
        count = 0;
    }

八、与HashMap比较

  • Hashtable是线程安全,而HashMap非线程安全
  • Hashtable的key和value都不可以)为null值;HashMap允许得key和value都可以为null值
  • Hashtable继承自Dictionary,而HashMap继承自AbstractMap
  • Hashtable使用Enumeration进行遍历;HashMap使用Iterator进行遍历
  • Hashtable在链表头部插入,HashMap(jdk8之后)在尾部插入
  • 存储位置计算方式不同,HashMap用与代替求模
  • 哈希值的使用不同,HashTable直接使用对象的hashCode; HashMap重新计算hash值
  • Hashtable每次扩容,新容量为旧容量的2倍加1,而HashMap为旧容量的2倍
  • Hashtable默认容量为 11,HashMap默认容量为16;Hashtable的容量为任意正数(最小为1),而HashMap的容量始终为2的n次方.
  • Hashtable和HashMap都是通过链表解决冲突;但HashMap引入了红黑树,Hashtable没有
  • Hashtable和HashMap默认负载因子都为0.75
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值