【JDK1.8源码阅读】Hashtable.class(完)

在看hashtable前你需要了解

Hashtable本质是一条线性的链表:
0、Hashtable是较早的JDK版本中的类,数据结构是`Entry<K, V> next`单向链表
1、哈希函数(直接寻址法、数字分析法、平方取中法、折叠法、随机数法、除留余数法)
2hashCode()的生成在Object类内置了方法
3、链表Entry<?, ?>[]的数据结构
4、如何插入和删除一个Entry
5、与HashMap的区别

祖先Dictionary(继承)

继承Dictionary<K,V>,本是抽象的key、value。内部全是抽象变量。

public abstract
class Dictionary<K,V> {
    public Dictionary() {  }
    abstract public int size();
    abstract public boolean isEmpty();
    abstract public Enumeration<K> keys();
    abstract public Enumeration<V> elements();
    abstract public V get(Object key);
    abstract public V put(K key, V value);
    abstract public V remove(Object key);
}

兄弟Map,Cloneable,Serializable(实现接口)

Cloneable和Serializable都是一个空接口。

  • Map<K, V>接口:定义变量的常规操作方法
  • Cloneable接口:clone是首先分配内存,分配的内存与调用clone方法对象的内存相同,然后将源对象中各个变量的值,填充到新的对象中,填充完成后,clone方法返回一个新的地址,这个新地址的对象与源对象相同,只是地址不同。
  • Serializable接口:赋予序列化能力

孩子hash、key、value

  • 从数据结构可以看出Hashtable是由Entry<K, V>组成的单向链表
private static class Entry<K, V> implements Map.Entry<K, V> {
        final int hash;//使用key的hashCode(),是Object类的native的方法
        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;
        }
  • 其它常量表述的含义
    private transient Entry<?, ?>[] table;//看1108行的数据结构
    private transient int count;//统计Entry的数量
    private int threshold;//addEntry的时候如果比count小,则调用rehash()增加容量,重新计算每个键值对的hashCode
    private float loadFactor;
    private transient int modCount = 0;
    private static final long serialVersionUID = 1421746759512286392L;
  • table:键值对数组,每个Entry本质上是一个单向链表的表头
  • count:当前表中的Entry数量,如果超过了阈值,就会扩容,即调用rehash方法
  • threshold:rehash阈值
  • loadFactor:负载因子
  • modCount :用来实现"fail-fast"机制的(也就是快速失败)。所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你(你已经出错了)。
  • serialVersionUID:版本序列号,序列化用的

究竟如何插入一个值

  • 确保key不在table,如果key对应的值存在,则用新值覆盖value
  • 如果key对应的值不存在,就调用addEntry方法加入到index位置
public synchronized V put(K key, V value) {
        if (value == null) {
            throw new NullPointerException();
            }
        //确保key不在table,如果key对应的值存在,则用新值覆盖value
        Entry<?, ?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        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;
            }
        }
        // 如果key对应的值不存在,就调用addEntry方法加入
        addEntry(hash, key, value, index);
        return null;
    }
  • tab[index]和HashMap不同,Hashtable选择把新插入的元素放到链表最前边
private void addEntry(int hash, K key, V value, int index) {
        modCount++;
        Entry<?, ?> tab[] = table;//重新增加一个数组
        if (count >= threshold) {
            rehash();// 当前元素大于等于阈值,就扩容并且再计算hash值
            tab = table;
            hash = key.hashCode();
            //0x7FFFFFFF 0111 1111 1111 1111 1111 1111 1111 1111
            index = (hash & 0x7FFFFFFF) % tab.length;//第一位是0表示正数,0与任何数都是0,1与任何数都是1,因此以此保证第一位是0,其后不变
        }
        @SuppressWarnings("unchecked")
        Entry<K, V> e = (Entry<K, V>) tab[index];// Creates the new entry.
        //和HashMap不同,Hashtable选择把新插入的元素放到链表最前边,而且没有使用红黑树
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

究竟删除插入一个值

  • e是要删除的节点,遍历链表,如果表中(e.hash == hash) && e.key.equals(key)则表示存在相同的节点,将其删除(前一个节点的next指向下一个节点)
 public synchronized V remove(Object key) {
        Entry<?, ?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K, V> e = (Entry<K, V>) tab[index];//e是要删除的节点
        for (Entry<K, V> 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;
    }

你应该知道的

Hashtable的key/value为什么不可为null

也许Hashtable类的设计者当时认为null作为key 和value 是没有什么用的。

HashMap对象的key、value值均可为null。
HashTable对象的key、value值均不可为null。

Hashtable比HashMap出生早

Hashtable命名没用驼峰规则,HashMap用上了。Hashtable现在项目使用频率很低。

参考:https://blog.csdn.net/u010297957/article/details/51974340

发布了99 篇原创文章 · 获赞 106 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/lglglgl/article/details/105145060