HashTable底层源码分析

HashTable底层源码分析

一、准备工作

package com.liu.map_;

import java.util.Hashtable;
import java.util.Map;

public class HashTable_debug {
    
    
    public static void main(String[] args) {
    
    
        Map<Object, Object> hashtable = new Hashtable<>();//断点1
        for (int i = 1; i < 10; i++) {
    
    
            hashtable.put(i,"number"+i);//断点2
        }
        System.out.println(hashtable);
    }
}

二、debug

默认构造函数

可以看出,hashtable默认初始化容量未11,默认扩容因子为0.75

/**
 * Constructs a new, empty hashtable with a default initial capacity (11)
 * and load factor (0.75).
 */
public Hashtable() {
    
    
    this(11, 0.75f);
}

进入 this(11, 0.75f),有参构造函数

/**
 * Constructs a new, empty hashtable with the specified initial
 * capacity and the specified load factor.
 *
 * @param      initialCapacity   the initial capacity of the hashtable.
 * @param      loadFactor        the load factor of the hashtable.
 * @exception  IllegalArgumentException  if the initial capacity is less
 *             than zero, or if the load factor is nonpositive.
 */
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);
	//若初始化容量为0,则将容量变为1
    if (initialCapacity==0)
        initialCapacity = 1;
    this.loadFactor = loadFactor;
    //新建一个 Entry<?,?>[initialCapacity]数组
    table = new Entry<?,?>[initialCapacity];
    threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

Hashtable$Entry

hashtable中的Entry数组的源码

实现了Map.Entry<K,V>,需要重写其所有方法,所以有getKey和getValue的方法

Entry<K,V> next;仅有一个next结点,没有pre结点,所以为单向链表

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

    @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();
    }
}

跳出Hashtale构造函数

Integer装箱

public static Integer valueOf(int i) {
    
    
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

put方法

synchronized:线程安全

hashtable的value值不能为空

遍历当前下标中的链表,看是否有key值相等的,若相等,则覆盖

/**
 * Maps the specified <code>key</code> to the specified
 * <code>value</code> in this hashtable. Neither the key nor the
 * value can be <code>null</code>. <p>
 *
 * The value can be retrieved by calling the <code>get</code> 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 <code>null</code> if it did not have one
 * @exception  NullPointerException  if the key or value is
 *               <code>null</code>
 * @see     Object#equals(Object)
 * @see     #get(Object)
 */
//线程安全
public synchronized V put(K key, V value) {
    
    
    // Make sure the value is not null
    //hashtable的值不能为空
    if (value == null) {
    
    
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    //计算数组下标
    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;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

addEntry

count初始为0,当count达到8时,满足count >= threshold(8),即为添加第9个元素时,进行扩容

count在加完新结点后才自增

采用头插法将新的结点插入到tab[index]的头部

头插法原理:e的位置为next指针,即为新节点的next指向e

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.
    @SuppressWarnings("unchecked")
    //e记录当前的链表头结点
    Entry<K,V> e = (Entry<K,V>) tab[index];
    //创建结点,采用头插法将新的结点插入到tab[index]的头部
    //原理   e的位置为next指针,即为新节点的next指向e
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
}

rehash()

扩容机制:2*oldCapacity+1

计算新的扩容容量:threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 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() {
    
    
    //length表示的是table数组的长度
    int oldCapacity = table.length;
    //记录table
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    //扩容机制:2*oldCapacity+1
    
    int newCapacity = (oldCapacity+1 << 1) + 1;
    //容量过大超过Integer.MAX_VALUE - 8
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
    
    
        if (oldCapacity == MAX_ARRAY_SIZE)
            // Keep running with MAX_ARRAY_SIZE buckets
            return;
        newCapacity = MAX_ARRAY_SIZE;
    }
    //创建并将新容量赋值给newMap
    Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

    modCount++;
    //计算新的扩容容量
    threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    //将新数组赋值给table
    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;
            old = old.next;

            int index = (e.hash & 0x7FFFFFFF) % newCapacity;
            e.next = (Entry<K,V>)newMap[index];
            newMap[index] = e;
        }
    }
}

add成功

三、总结(重点)

1、hashtable默认初始化容量未11,默认扩容因子为0.75

2、 Entry<K,V> next;仅有一个next结点,没有pre结点,所以为单向链表

3、synchronized:线程安全

4、hashtable的value值不能为空

5、遍历当前下标中的链表,看是否有key值相等的,若相等,则覆盖

6、count初始为0,当count达到8时,满足count >= threshold(8),即为添加第9个元素时,进行扩容

count在加完新结点后才自增

采用头插法将新的结点插入到tab[index]的头部

头插法原理:e的位置为next指针,即为新节点的next指向e

7、扩容机制:2*oldCapacity+1

计算新的扩容容量:threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

猜你喜欢

转载自blog.csdn.net/ABidMan/article/details/121138705