If anyone asked me to describe HashMap's working mechanism, I would simply answer: "Hash based rules." This sentence is very simple, but to understand this sentence, first we have to understand what is a hash, is not it?
What is a hash
a hash is simply a unique string that is obtained by applying an algorithm to a variable/object attribute, using this string to determine the uniqueness of the variable/object. A correct hash function must adhere to this guideline.
When a hash function is applied to the same object or to a equal object, each execution should return the same value. In other words, two equal objects should have the same hashcode.
Note: All Java objects inherit a default Hashcode () method from the object class. This method returns the object's address in memory as an integer, which is a good hash implementation, ensuring that different objects have different hashcode.
A little introduction to the entry class
a map is defined as an object that maps a key (key) to a value. It's very simple, right.
Therefore, there must be some mechanism in HashMap to store these key-value pairs. So, HashMap has an inner class entry that looks like this.
Static class Entry<k,v> implements
map.entry<k,v>
{
final K key;
V value;
Entry<k,v> Next;
final int hash;
...//more code goes here
}
Of course, the entry class has attributes to store the key-value pair mappings. Key is final and we see two variables next and hash in addition to key and value. Next we try to understand the meaning of these variables.
the put () method actually does something
before further looking at the implementation of the Put method, it is necessary to look at the storage of the entry instance in the array, which is defined in HashMap:
/** * The table, resized as necessary.
Length must Always is a power of two.
* * transient entry[] table;
Now let's look at the implementation of the Put method.
/** * Associates The specified value with the specified key into this map.
* If The map previously contained a mapping for the "key", the old * value is replaced. * * @param key key with which the specified value being associated * @param value value to being associated with the Speci Fied Key * @return The previous value associated with <tt>key</tt>, or * &
nbsp; <tt>null</tt> If there is no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate the map *&N bsp; previously associated <tt>null</tt> with <tt>key
</tt>.) * * Public V-put (K key, V value) {if (key = = null) return Putfornullkey (value); int hash = hash (Key.hashcode ()); int i = iNdexfor (hash, table.length); for (entry<k,v> e = table[i]; e!= null; e = e.next) {Object K; if (E.hash = = Hash && (K = e.key) = = Key | |
Key.equals (k))) {V oldValue = e.value; e.value = value; e.recordaccess (this); return oldValue;}
modcount++;
AddEntry (hash, key, value, I);
return null;
}
Let's take a step at a glance
First, check to see if the key is null, if the key is the position of the null value being table[0] because null hashcode is always 0
Next, the hash value of the key is computed by the hashcode () method of the key, which is used to compute the position of the array where the entry object is stored. The JDK designers assume that there are some people who might write very bad hashcode () methods, and some very large or very small hash values will appear. To solve this problem, they introduced another hash function that accepted the object's Hashcode () and converted it to fit the array's size.
followed by the Indexfor (Hash,table,length) method, which calculates the exact location of the entry object's storage.
The next is the main part, we all know that two unequal objects may have the same hashcode value, how two different objects are stored in the same place [called bucket]?
The answer is LinkedList. If you remember, the entry class has a next variable that always points to the next variable in the chain, which exactly matches the list's characteristics.
So, in the event of a collision, the entry object is stored as a linked list, and when a entry object needs to be stored, HashMap checks to see if there is a entry object in the position, and if so, check her next property, if it is empty, The current entry object acts as the next node of the entry object that is already stored, and so on.
What happens if we deposit another value on a key that already exists? Logically, the old values will be replaced. After detecting the storage location of the entry object, HashMap will traverse the entry list in that location, calling the Equals method for each entry, all objects in the list have the same hashcode () and the Equals method is unequal. If the Equals method is found to be equal, the substitution is performed.
In this way HashMap can guarantee the uniqueness of the key.
The working mechanism of the Get method
Now we have a look at the mechanism for storing key-value pairs in HashMap. The next question is: How to query the results from a hashmap.
Logic is the same as put, if the incoming key has a match, return the value of that location, or null if not.
/** * Returns the value to which the specified key was mapped, * or {@code null} If this map contains no mapping for the K
ey. * * <p>more formally, if this map contains a mapping from a key * {@code k} to a value {@code V} such that {@code K Ey==null? K==null: * Key.equals (k))}, then this method returns {@code V}; otherwise * It returns {@code null}. (There can be in most one such mapping.) * * <p>a return value of {@code NULL} does not <i>necessarily</i> * indicate so the map contains no mapping for the key;
It's also * possible this map explicitly maps the key to {@code null}.
* The {@link #containsKey ContainsKey} operation May is used to * distinguish these two cases. * * @see #put (object, Object) */Public V get (object key) {if (key = null) return Getfornullkey (); int hash = hash (key.h
Ashcode ()); for (entry<k,v> e = table[indexfor (hash, table.length)]; e!= null; e = e.next) {Object K; if (E.hash = hash & & ((k = e.key) = = key ||
Key.equals (k)) return e.value;
return null;
}
The code above looks much like the put () method except if (E.hash = = Hash && (k = e.key) = = Key | | key.equals (k)).
Pay attention.
- The data structure that stores the entry object is a table array called the entry type.
- A particular index position in an array is called bucket because it can hold an object of the first element of a linkedlist.
- The Hashcode () of the key object needs to be used to compute the storage location of the entry object.
- The Equals () method of a key object needs to be used to maintain uniqueness of the objects in the map.
- The Get () and put () methods are independent of the hashcode and equals methods of the value object.
- The hashcode of NULL is always 0, and such entry objects are always stored in the first position of the array