Can the key of HashMap be a mutable object ???, Hashmapkey
As we all know, HashMap is composed of key-value pairs. This key can be a basic data type object, such as Integer, Float, at the same time, it can also be self-written objects. The question is, can this object as the key be changed? Or can a key be a variable object? What if the HashMap can be used?
Variable object
Variable objectAn object whose status can be changed after being created. In other words, the variable object isThis objectAfter it is createdHash Value(The hash value can be obtained by the hashCode () method of the Class)May be changed.
In order to intuitively see the changes in the hash value, a class is written below, and the hashCode () method of the class and its equals () method are rewritten () method [For more information about how to rewrite the equals method, visit the blog: http://www.cnblogs.com/0201zcr/p/4769108.html#. The equals method is used when you search for and add (put method.
In the following code, when the key of the MutableKey object is created, the variable I = 10 j = 20 and the hash value is 1291.
Then we change the variable value of the instance. The key I and j of the object are changed from 10 and 20 to 30 and 40, respectively. Now, the hash value of the Key has changed to 1931.
Obviously, the key of this object has changed after it is created. So the MutableKey class is variable.
Let's take a look at the following sample code:
public class MutableKey { private int i; private int j; public MutableKey(int i, int j) { this.i = i; this.j = j; } public final int getI() { return i; } public final void setI(int i) { this.i = i; } public final int getJ() { return j; } public final void setJ(int j) { this.j = j; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + i; result = prime * result + j; return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof MutableKey)) { return false; } MutableKey other = (MutableKey) obj; if (i != other.i) { return false; } if (j != other.j) { return false; } return true; }}
Test:
public class MutableDemo { public static void main(String[] args) { // Object created MutableKey key = new MutableKey(10, 20); System.out.println("Hash code: " + key.hashCode()); // Object State is changed after object creation. key.setI(30); key.setJ(40); System.out.println("Hash code: " + key.hashCode()); }}
Result:
Hash code: 1291
Hash code: 1931
As long as the member variable I or j of the MutableKey object changes, the hash value of the object changes, so the object is a variable object.
How does HashMap store key-value pairs?
HashMap is stored in an array of Entry objects at the underlying layer, while Entry is a single linked list. If you call a put () method to add a key-value pair, use the hash () function to obtain the hash value of the object, then, call the indexFor method to find the subscript that the object should store in the array. If the position is null, insert the value. If the value is not empty, traverse the objects above the subscript and use the equals Method for judgment. If the equals () method returns true, replace it. Otherwise, insert it. For details about the source code, see http://www.cnblogs.com/0201zcr/p/4769108.html.
When searching, you only need to query and obtain the hash value through the key value, find its subscript, and traverse the Entry object under the subscript to find the value. [For details, refer to the following source code and its explanation]
Problems caused by using variable objects as keys in HashMap
If the hash value of the HashMap Key changes after the Key-value pair, the Map may no longer find this Entry..
Public V get (Object key) {// if the key is null, call getForNullKey to retrieve the corresponding value if (key = null) return getForNullKey (); // calculate its hash code int hash = hash (key. hashCode (); // directly retrieve the value of the specified index in the table array. for (Entry <K, V> e = table [indexFor (hash, table. length)]; e! = Null; // search for the next Entr e = e of the Entry chain. next) // ① {Object k; // if the key of the Entry is the same as the searched key if (e. hash = hash & (k = e. key) = key | key. equals (k) return e. value;} return null ;}
The above is the source code of the get () method of HashMap. Through the above, we can know that ifOnly one Entry,HashMap can quickly retrieve the entries in the bucket according to the index; In the case of a "Hash Conflict,A single bucket stores not an Entry, but an Entry chain.,The system can only traverse each Entry in order until the Entry to be searched is found.-- If the Entry to be searched is located at the end of the Entry chain (the Entry is first placed in the bucket), the system must loop to the end to find the element.
At the same time, we can see that,To determine whether the object is found, we also need to determine whether its hash value is the sameIf the hash values are different, we cannot find the value.
If the Key object is variable, the hash value of the Key may change. Changing an object as a Key in HashMap results in data loss.
The example below will show you the problems caused by the variable object in HashMap as the Key.
import java.util.HashMap;import java.util.Map; public class MutableDemo1 { public static void main(String[] args) { // HashMap Map<MutableKey, String> map = new HashMap<>(); // Object created MutableKey key = new MutableKey(10, 20); // Insert entry. map.put(key, "Robin"); // This line will print 'Robin' System.out.println(map.get(key)); // Object State is changed after object creation. // i.e. Object hash code will be changed. key.setI(30); // This line will print null as Map would be unable to retrieve the // entry. System.out.println(map.get(key)); }}
Output:
Robinnull
Solution
Use immutable objects in HashMap. In HashMap, it is wise to use an unchangeable type such as String and Integer as the Key.
We alsoCan define your own immutable class.
If a variable object is used as a key in HashMap, be careful not to change its hash value when changing the object state.We only need to ensure that the modification of the member variable can ensure that the hash value of the object remains unchanged.
In the following example class, the hash value is calculated using the instance variable id.Once the Employee object is created, the id value cannot be changed.. Only the name can be changed, but the name cannot be used to calculate the hash value. So,Once the Employee object is created, its hash value will not change. Therefore, it is safe to use the Employee as a Key in HashMap.
import java.util.HashMap;import java.util.Map; public class MutableSafeKeyDemo { public static void main(String[] args) { Employee emp = new Employee(2); emp.setName("Robin"); // Put object in HashMap. Map<Employee, String> map = new HashMap<>(); map.put(emp, "Showbasky"); System.out.println(map.get(emp)); // Change Employee name. Change in 'name' has no effect // on hash code. emp.setName("Lily"); System.out.println(map.get(emp)); }} class Employee { // It is specified while object creation. // Cannot be changed once object is created. No setter for this field. private int id; private String name; public Employee(final int id) { this.id = id; } public final String getName() { return name; } public final void setName(final String name) { this.name = name; } public int getId() { return id; } // Hash code depends only on 'id' which cannot be // changed once object is created. So hash code will not change // on object's state change @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (id != other.id) return false; return true; }}
Output
ShowbaskyShowbasky
Thank you: Thank you for your patience!