The specific process of get(K key) and put(K key, V value) in HashMap

Keywords: Java REST

Said ahead

This article contains the handwritten generic HashMap < K, V > as a simplified version, only to understand the working process of get() and put() methods of HashMap, not Java source code.

get(K key) principle

  1. First calculate the hash value corresponding to the key
    int hash = key.hashCode();
    //The hashCode() method here is the method of the Object object, and it is available by default
    //Custom class needs to override this method
  2. Handling hash values out of array range
    hash = (hash >>> 16)^hash;//java internal self-made optimization, in order to make hash value more balanced and reduce conflicts
    int index = hash & (table.length - 1);//Rationalize the subscript to avoid the subscript crossing the boundary
    //This can make index in the range of array length. One of the reasons or a premise is that the array length here must be the nth power of 2,
    //In this way, table.length - 1 in binary case, except for the highest bit, the rest of the low bit must be 1. Use hash and such a number for operation
    //That is to say, if only the low bit of hash is reserved, the hash range must be smaller than the array length
  3. Find the head node of the linked list according to the correct hash value (subscript value)
    Entry<K,V> node = table[index];
  4. Traverse the linked list. If the key values are equal, return the corresponding value. Otherwise, return null
while(node != null){
    if(node.key.equals(key)){
        return node.value;
    }
    node = node.next;
}
  • Specific implementation get(K key)

@Override
    public V get(K key) {
        int hash = key.hashCode();
        hash = (hash >>> 16)^hash;//Self made optimization in java to make hash value more balanced
        int index = hash & (table.length - 1);
        Entry<K,V> node = table[index];
        while(node != null){
            if(node.key.equals(key)){
                return node.value;
            }
            node = node.next;
        }
        return null;
    }

put(K key,V value) principle

  1. First calculate the hash value corresponding to the key
  2. Handling hash values out of array range
  3. Find the head node of the linked list according to the correct hash value (subscript value)
  4. If the header node = = null, assign the new node directly to this position of the array
    Entry<K,V> newNode = new Entry<>(key,value);
    table[index] = newNode;
  5. Otherwise, traverse the list, find the node with the same key, replace the value value, and return the old value
    Entry<K,V> pre = null;//It is used to track the last node of the link list, and prepare for tail insertion. If head insertion is used, it is not necessary
    while(node != null){
    if(node.key.equals(key)){
        V oldValue = node.value;
        node.value = value;
        return oldValue;
    }
    pre = node;
    node = node.next;
    }
  6. If it is not found, create a new node and insert it into the linked list by tail insertion (1.8) / head insertion (1.7)
    pre.next = new Entry<>(key,value);
  7. Number of storage elements + 1!!!
  8. Verify whether expansion is needed (all hash values need to be recalculated because the array length has changed)
    1) Reason for expansion: in order to reduce hash conflict and conflict rate (when inserting a new key, the conflict probability will be encountered)
    2) Load factor = number of all key s / length of array
    3) Conflict rate is positively correlated with load factor! Therefore, in order to reduce the conflict rate, you can change the length of the array!
    4) Specific operation: calculate the load factor and compare it with the expansion factor (specified as 0.75)
    if((double) size / table.length >= 0.75 ){
        resize();
    }
private void resize(){
    /**
    * 1.Create a new array, twice the length of the original array
    * 2.Traverse the original array to find the head node of each linked list
    * 3.Traverse each linked list, create a new node and insert the node into the new array by using the head insertion method
    *
    */
    Entry<K,V>[] newTable = new Entry[table.length * 2];
    for(int i = 0; i < table.length; i++){
        Entry<K,V> node = table[i];
        while(node != null){
            Entry<K,V> newNode = new Entry<>(node.key,node.value);
            int hash = node.key.hashCode();
            hash = (hash >>> 16) ^ hash;
            int index = hash ^ (newTable.length - 1);
            //Use the head plug and the tail plug
            newNode.next = newTable[index];
            newTable[index] = newNode;
            node = node.next;
        }
    }
}
  • Specific implementation put(K key,V value)

@Override
public V put(K key, V value) {
    int hash = key.hashCode();
    hash = (hash >>> 16)^hash;//In order to make the hash value more balanced, java optimizes its own statements
    int index = hash & (table.length - 1);
    Entry<K,V> node = table[index];
    if(node == null){
        Entry<K,V> newNode = new Entry<>(key,value);
        table[index] = newNode;
    }else{
        Entry<K,V> pre = null;
        while(node != null){
            if(node.key.equals(key)){
                V oldValue = node.value;
                node.value = value;
                return oldValue;
            }
            pre = node;
            node = node.next;
        }
        pre.next = new Entry<>(key,value);
    }
    size++;
    if((double) size / table.length >= LOAD_FACTOR_THRESHOLD ){
        resize();
    }
    return null;
}

The specific implementation of HashMap < K, V >

package advance_ds.hashmap;
//Interface
public interface Map<K,V> {
    V get(K key);

    V put(K key,V value);

}

/**
 * @author Maria
 * @program JavaDaily
 * @date 2020/3/21 14:51
 */
public class HashMap<K,V> implements Map<K,V> {
    //Node class of linked list
    private static class Entry<K,V>{
        K key;
        V value;
        Entry<K,V> next;

        public Entry(K key,V value){
            this.key = key;
            this.value = value;
        }
    }

    //Basic storage mode: array
    private Entry<K,V>[] table = new Entry[16];
    //Number of elements stored
    private int size = 0;
    //Dilatation factor
    private static final double LOAD_FACTOR_THRESHOLD = 0.75;

    @Override
    public V get(K key) {
        /**
         * 1.First calculate the hash value corresponding to the key
         * 2.Handling hash values out of array range
         * 3.Find the head node of the linked list according to the correct hash value (subscript value)
         * 4.Traverse the linked list. If the key values are equal, return the corresponding value. Otherwise, return null
         */
        int hash = key.hashCode();
        hash = (hash >>> 16)^hash;//java internal self-made optimization, in order to make the hash value more balanced
        int index = hash & (table.length - 1);
        Entry<K,V> node = table[index];
        while(node != null){
            if(node.key.equals(key)){
                return node.value;
            }
            node = node.next;
        }
        return null;
    }

    @Override
    public V put(K key, V value) {
        /**
         * 1.First calculate the hash value corresponding to the key
         * 2.Handling hash values out of array range
         * 3.Find the head node of the linked list according to the correct hash value (subscript value)
         * 4.If the header node = = null, assign the new node directly to this position of the array
         * 5.Otherwise, traverse the list, find the node with the same key, replace the value value, and return the old value
         * 6.If it is not found, create a new node and insert it into the linked list by tail insertion (1.8) / head insertion (1.7)
         * 7.Number of storage elements + 1
         * 8.Verify whether expansion is needed (all hash values need to be recalculated because the array length has changed)
         *      Reason for expansion: in order to reduce hash conflicts, conflict rate: the probability of conflict when a new key is inserted
         *          Load factor = number of all key s / length of array
         *          Conflict rate is positively correlated with load factor! Therefore, in order to reduce the conflict rate, you can change the length of the array!
         *      Specific operation: calculate the load factor and compare with the expansion factor
         */

        int hash = key.hashCode();
        hash = (hash >>> 16)^hash;//In order to make the hash value more balanced, java optimizes its own statements
        int index = hash & (table.length - 1);
        Entry<K,V> node = table[index];
        if(node == null){
            Entry<K,V> newNode = new Entry<>(key,value);
            table[index] = newNode;
        }else{
            Entry<K,V> pre = null;
            while(node != null){
                if(node.key.equals(key)){
                    V oldValue = node.value;
                    node.value = value;
                    return oldValue;
                }
                pre = node;
                node = node.next;
            }
            pre.next = new Entry<>(key,value);
        }
        size++;
        if((double) size / table.length >= LOAD_FACTOR_THRESHOLD ){
            resize();
        }
        return null;

    }

    private void resize(){
        /**
         * 1.Create a new array, twice the length of the original array
         * 2.Traverse the original array to find the head node of each linked list
         * 3.Traverse each linked list, create a new node and insert the node into the new array by using the head insertion method
         *
        */
        Entry<K,V>[] newTable = new Entry[table.length * 2];
        for(int i = 0; i < table.length; i++){
            Entry<K,V> node = table[i];
            while(node != null){
                Entry<K,V> newNode = new Entry<>(node.key,node.value);
                int hash = node.key.hashCode();
                hash = (hash >>> 16) ^ hash;
                int index = hash ^ (newTable.length - 1);
                //Use the head plug and the tail plug
                newNode.next = newTable[index];
                newTable[index] = newNode;
                node = node.next;
            }
        }
    }
}

Write a class to test this code

package advance_ds.hashmap;

import java.util.Objects;

/**
 * @author Maria
 * @program JavaDaily
 * @date 2020/3/21 21:29
 */
public class Person {
    private String name;
    private int age;
    private int gender;

    //Automatically generated
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name) &&
                Objects.equals(gender, person.gender);
    }
    //Automatically generated
    @Override
    public int hashCode() {
        return Objects.hash(name, age, gender);
    }

    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "p1";
        p1.age = 18;
        p1.gender = 0;
        Person p2 = new Person();
        p2.name = "p1";
        p2.age = 18;
        p2.gender = 0;
        HashMap<Person,Integer> map = new HashMap<>();
        map.put(p1,108);
        System.out.println(map.get(p2));//The result is 108, which is taken out successfully! Because the hash corresponding to key is equal
    }
}

Posted by wyrd33 on Sun, 22 Mar 2020 03:39:17 -0700