HashTable underlying source code analysis

HashTable underlying source code analysis

1. Preparation work

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

Second, debug

default constructor

It can be seen that the default initialization capacity of hashtable is not 11, and the default expansion factor is 0.75.

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

Enter this(11, 0.75f), parameterized constructor

/**
 * 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

Source code of Entry array in hashtable

Map.Entry<K,V> is implemented and all its methods need to be rewritten, so there are getKey and getValue methods.

Entry<K,V> next; there is only one next node and no pre node, so it is a one-way linked list

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

Jump out of Hashtale constructor

Integer boxing

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

put method

synchronized: thread safety

The value of hashtable cannot be empty

Traverse the linked list in the current subscript to see if there are keys with equal values. If they are equal, overwrite them.

/**
 * 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

The count is initially 0. When the count reaches 8, it satisfies count >= threshold (8), that is, when the 9th element is added, the capacity is expanded.

count is incremented after adding new nodes.

Use the head insertion method to insert the new node into the head of tab[index]

Principle of head interpolation method: The position of e is the next pointer, that is, the next point of the new node points to 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()

Expansion mechanism: 2*oldCapacity+1

Calculate the new expansion capacity: threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

Recalculate the subscript and assign it to the new array

/**
 * 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 successfully

3. Summary (key points)

1. The default initialization capacity of hashtable is not 11, and the default expansion factor is 0.75.

2. Entry<K,V> next; has only one next node and no pre node, so it is a one-way linked list

3. synchronized: thread safety

4. The value of hashtable cannot be empty.

5. Traverse the linked list in the current subscript to see if there is an equal key value. If so, overwrite it.

6. The count is initially 0. When the count reaches 8, it satisfies count >= threshold (8), that is, when the ninth element is added, the capacity is expanded.

​Count is incremented after adding new nodes.

Use the head insertion method to insert the new node into the head of tab[index]

Principle of head

7. Capacity expansion mechanism: 2*oldCapacity+1

Calculate the new expansion capacity: threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);

Guess you like

Origin blog.csdn.net/ABidMan/article/details/121138705