【jdk8源码】HashTable集合

概述

HashTable和HashMap类似,底层都是使用数组+单向链表实现,只是HashMap中多了一个转化红黑树的过程。HashTable不允许键值为空、遍历时无序、键不可重复、线程安全(在常用方法介绍里面可以看到,基本的操作方法例如put()、remove()等都使用了synchronized同步修饰)。

继承关系

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
  • 继承Dictionary类:Dictionary 类是任何可将键映射到相应值的类的抽象父类,Dictionary类声明了操作键值对的接口方法
  • 实现Map接口:提供了map操作的一些方法供实现,下面会说到,主要的方法都是实现Map接口中的
  • 实现Cloneable接口:为了使用Object.clone()方法,不实现该接口会报错。
  • 实现Serializable接口:表示该类可以被序列化

数据结构

    //Entry集合,用来存放数据
    private transient Entry<?,?>[] table;
    //内部类Entry
    private static class Entry<K,V> implements Map.Entry<K,V> {
        //Entry的hash值
        final int hash;
        //Entry的键,Key
        final K key;
        //Entry的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;
        }
        //重写clone()方法
        @SuppressWarnings("unchecked")
        protected Object clone() {
            return new Entry<>(hash, key, value,
                                  (next==null ? null : (Entry<K,V>) next.clone()));
        }

        //获取key
        public K getKey() {
            return key;
        }
        //获取value
        public V getValue() {
            return value;
        }
        //设置value
        public V setValue(V value) {
        //value为空抛出异常
            if (value == null)
                throw new NullPointerException();
            //设置新value返回旧的value
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
        //重写equals比较方法
        public boolean equals(Object o) {
            //先判断是不是Entry类型,如果不是直接返回false
            if (!(o instanceof Map.Entry))
                return false;
            //如果是Entry类型,先判断键是不是相等,再判断值是不是相等,都相等才返回true
            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()));
        }
       //hash值计算,调用Object的hashCode计算出hash值,再和原本的hash进行异或运算
        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }
        //重写toString方法
        public String toString() {
            return key.toString()+"="+value.toString();
        }
    }

成员变量


     //Entry类型数组,存放键值对的数据,每个位置上存放的是Entry的首节点
    private transient Entry<?,?>[] table;
    //当前Entry数组中的数量
    private transient int count;
    //触发扩容的值,用来判断是否需要进行扩容
    private int threshold;
    //负载因子,就是允许数组中存放的比例,当数组中元素的个数超过了总容量*负载因子时,触发扩容
    private float loadFactor;
    //记录修改次数,并发时用于判断是否有其他线程更改了数据
    private transient int modCount = 0;
    //序列化编号
    private static final long serialVersionUID = 1421746759512286392L;
    //数组允许的最大长度,等于Integer的最大值-8,和ArrayList一样
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //key键集合,不允许重复
    private transient volatile Set<K> keySet;
    //键值对集合,不允许重复
    private transient volatile Set<Map.Entry<K,V>> entrySet;
    //value值集合,允许重复
    private transient volatile Collection<V> values;
    //静态常量
    //迭代器中用到的type,Enumerations、Iterations
    private static final int KEYS = 0;
    private static final int VALUES = 1;
    private static final int ENTRIES = 2;

构造方法

  • 入参集合的长度和负载因子
 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;
        //把负载因子指向loadFactor 
        this.loadFactor = loadFactor;
        //初始化数组
        table = new Entry<?,?>[initialCapacity];
        //设置threshold 值为数组长度*负载因子-最大值之间的最小值
        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<? extends K, ? extends V> t) {
       //集合长度取tde长度*2-11之间的最大值,负载因子是0.75
        this(Math.max(2*t.size(), 11), 0.75f);
        //putAll方法下面会详细说
        putAll(t);
    }

扩容和hash计算

  • hash计算
//在put方法中有
 int hash = key.hashCode();
//在hash为负值的情况下,以和0x7FFFFFFF进行&操作。0x7FFFFFFF 二进制 0111 1111 1111 1111 1111 1111 1111 1111,负数与其进行&操作将产生一个正整数。
 int index = (hash & 0x7FFFFFFF) % tab.length;
  • 扩容
   //扩容
    protected void rehash() {
    //获取原数组大小和原数组元素
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        //新的大小=原数组*2+1,即原数组的两倍+1
        //HashMap是扩容到原来的两倍,ArrayList是1.5倍
        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 的值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        //将新的数组指向table 
        table = newMap;
       //循环数组
        for (int i = oldCapacity ; i-- > 0 ;) {
        //循环数组内的entry元素
            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.get(Object key)

    //通过key获取value
    public synchronized V get(Object key) {
        //获取原Entry数组
        Entry<?,?> tab[] = table;
        //得到key的hash值
        int hash = key.hashCode();
        //计算出key所在的index位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //循环遍历index处的entry
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
        //如果有hash相等且key也相等的,返回value值
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        //否则返回null
        return null;
    }

2.put(K key, V value)

//设置一对键值到集合中
   public synchronized V put(K key, V value) {
        //value为null抛出异常
        if (value == null) {
            throw new NullPointerException();
        }
        //获取元Entry数组
        Entry<?,?> tab[] = table;
        //计算key的hash值
        int hash = key.hashCode();
        //通过hash值计算key在table中的index位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        //获取entry元素
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        //如果元素不为空,说明hash碰撞,循环遍历获取到的entry
        for(; entry != null ; entry = entry.next) {
        //如果key已经存在,覆盖原来的值,返回旧的值
            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++;
        //获取原table
        Entry<?,?> tab[] = table;
        //如果数组中的元素已经大于threshold
        if (count >= threshold) {
            //调用rehash扩容,上面说了
            rehash();
            //重新获取扩容后的table
            tab = table;
            //计算key的hashCode,并获取其在数组中的index位置
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        //获取当前位置的元素
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        //调用Entry的构造方法,将元素插入为entry首节点,设置table中index的指向
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }
 

3.putAll(Map<? extends K, ? extends V> t)

  //内部调用put方法
    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());
    }

4.contains(Object value)

//判断value是否已经存在
    public synchronized boolean contains(Object value) {
    //value为空抛出异常
        if (value == null) {
            throw new NullPointerException();
        }
       //获取原数组
        Entry<?,?> tab[] = table;
        //循环遍历,如果找到了相同的value,返回true
        for (int i = tab.length ; i-- > 0 ;) {
            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        //否则返回false
        return false;
    }

5.replace(K key, V oldValue, V newValue)

    //替换指定key-value的value
    public synchronized boolean replace(K key, V oldValue, V newValue) {
        //先做非空判断
        Objects.requireNonNull(oldValue);
        Objects.requireNonNull(newValue);
        //获取原table
        Entry<?,?> tab[] = table;
        //计算key的hash值,并通过hash获取对应的index
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        //获取index处的元素
        Entry<K,V> e = (Entry<K,V>)tab[index];
        //如果元素不为空,循环遍历
        for (; e != null; e = e.next) {
        //如果有hash相等、key相等、value也相等的元素
            if ((e.hash == hash) && e.key.equals(key)) {
                if (e.value.equals(oldValue)) {
                //将旧的元素value替换成新的value,返回true
                    e.value = newValue;
                    return true;
                } else {
                //否则返回false
                    return false;
                }
            }
        }
        //否则返回false
        return false;
    }

6.remove

  • remove(Object key)
//根据指定的key删除元素
    public synchronized V remove(Object key) {
        //获取原table数据
        Entry<?,?> tab[] = table;
        //计算key的hash值,通过hash值计算出index
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        //获取index处的元素
        Entry<K,V> e = (Entry<K,V>)tab[index];
        //循环遍历e,prev用来记录上一个元素,处理删除后的链接
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            //如果hash值相等,key也相等
            if ((e.hash == hash) && e.key.equals(key)) {
            //记录修改次数
                modCount++;
                //判断是否是第一个节点
                if (prev != null) {
                //如果不是第一个节点,把删除节点的next赋值给删除节点的上一个节点
                    prev.next = e.next;
                } else {
                //如果是第一个节点,直接把e的next节点指向table的index位置
                    tab[index] = e.next;
                }
                //集合中的key-value数量-1
                count--;
                //将原节点e的value赋值为null,返回旧的值
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }
  • remove(Object key, Object value)
//根据指定的key-value删除元素
 public synchronized boolean remove(Object key, Object value) {
        //做非空判断
        Objects.requireNonNull(value);
        //步骤和上面的方法是一样的
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for (Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
        //只是这里判断的时候多加了一个value的判断
            if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                e.value = null;
                return true;
            }
        }
        return false;
    }

7.clear()

  //清空集合中的所有元素
  public synchronized void clear() {
  //获取原table
        Entry<?,?> tab[] = table;
        modCount++;
        //循环遍历,清空table的所有元素
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
            //设置count
        count = 0;
    }

8.size()

//返回集合中key-value个数
    public synchronized int size() {
        return count;
    }

9.isEmpty()

//判断集合是否为空
    public synchronized boolean isEmpty() {
        return count == 0;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值