源码阅读 - HashTable

0. HashTable是什么

  • 继承Dictionary
  • put get remove等方法是synchronized修饰
  • 存储类型

1. 主要数据结构

使用Entry数组存储数据
使用链表解决哈希冲突

/**
 * The hash table data.
 */
private transient Entry[] table;

/**
 * Hashtable bucket collision list entry
 */
private static class Entry implements Map.Entry {
    final int hash;
    final K key;
    V value;
    Entry next;
    //省略部分代码
    ....
}

2. 主要api解析

2.1 构造函数

默认的初始容量是11,装载因子是0.75
如果参数是Map初始容量是Max(原容量的2倍, 11)

public Hashtable()
public Hashtable(int initialCapacity)
public Hashtable(int initialCapacity, float loadFactor)
public Hashtable(Map t)

2.2 put方法

Neither the key nor the value can be null

从方法注释中可以看到Key Value均不能为null

/**
 * Maps the specified key to the specified
 * value in this hashtable. Neither the key nor the
 * value can be null. 

* * The value can be retrieved by calling the get method * with a key that is equal to the original key. * * @param key the hashtable key * @param value the value * @return the previous value of the specified key in this hashtable, * or null if it did not have one * @exception NullPointerException if the key or value is * null * @see Object#equals(Object) * @see #get(Object) */ public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry tab[] = table; int hash = key.hashCode(); //求取元素的index int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry entry = (Entry)tab[index]; for(; entry != null ; entry = entry.next) { //找到key相同的节点,说明以前存储过,直接更新value 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) { modCount++; 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. //容量未达到阈值,或者已经处理过,新建一个节点,插入当前index链的最前 @SuppressWarnings("unchecked") Entry e = (Entry) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; }

其中关于threshold部分,一般情况下不会有MAX_ARRAY_SIZE这么大,所以还是容量*装载因子居多。

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

2.3 get方法

直接使用index查找,使用equals判断

/**
 * Returns the value to which the specified key is mapped,
 * or {@code null} if this map contains no mapping for the key.
 *
 * 

More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key.equals(k))}, * then this method returns {@code v}; otherwise it returns * {@code null}. (There can be at most one such mapping.) * * @param key the key whose associated value is to be returned * @return the value to which the specified key is mapped, or * {@code null} if this map contains no mapping for the key * @throws NullPointerException if the specified key is null * @see #put(Object, Object) */ @SuppressWarnings("unchecked") public synchronized V get(Object key) { Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }

2.4 remove方法

从链中移除对应的entry

/**
 * Removes the key (and its corresponding value) from this
 * hashtable. This method does nothing if the key is not in the hashtable.
 *
 * @param   key   the key that needs to be removed
 * @return  the value to which the key had been mapped in this hashtable,
 *          or null if the key did not have a mapping
 * @throws  NullPointerException  if the key is null
 */
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];
    for(Entry prev = null ; e != null ; prev = e, e = e.next) {
        //找到对应的节点
        if ((e.hash == hash) && e.key.equals(key)) {
            modCount++;
            //如果是中间的节点,pre链上next
            if (prev != null) {
                prev.next = e.next;
            //如果是头节点,tab[index]链上next
            } else {
                tab[index] = e.next;
            }
            count--;
            V oldValue = e.value;
            e.value = null;
            return oldValue;
        }
    }
    return null;
}

2.5 扩容

  • 扩容时容量扩大为2*n+1大小
/**
 * Increases the capacity of and internally reorganizes this
 * hashtable, in order to accommodate and access its entries more
 * efficiently.  This method is called automatically when the
 * number of keys in the hashtable exceeds this hashtable's capacity
 * and load factor.
 */
@SuppressWarnings("unchecked")
protected void rehash() {
    int oldCapacity = table.length;
    Entry[] oldMap = table;
    // overflow-conscious code
    //容量扩大为2*n+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;
    for (int i = oldCapacity ; i-- > 0 ;) {
        //将旧的map中的数据逐一放到新的map中,旧的entry的index在新的map中可能会不同,因此会重新计算
        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;
        }
    }
}

3. 与HashMap的区别

  • HashTable
    1. Key Value都不能为null
    2. 方法是synchronized修饰的,即线程安全的
    3. 定位元素的index是取余的方式,因为长度不是2的幂
    4. 没有使用红黑树
    5. 插入节点时,HashTable是插入到链的最前,HashMap是插入到链的最后
    6. 扩容时变为2*n+1HashMap扩大为原来的2倍

4. 参考

  1. HashTable源码build 1.8.0_121-b13版本

你可能感兴趣的:(源码阅读 - HashTable)