On the Hashcode method in Java

Source: Internet
Author: User

Hash table This data structure presumably most people are not unfamiliar, and in many places will use hash tables to improve the search efficiency. There is a method in the object class in Java:

public native int hashcode ();

According to the declaration of this method, the method returns a numeric value of type int and is a local method, so no specific implementation is given in the object class.

Why does the object class need such a method? What role does it have? Today we are going to discuss the Hashcode method specifically.

I. The role of the Hashcode method

For programming languages that contain container types, hashcode is basically involved. As in Java, the Hashcode method is primarily intended to work with hash-based collections, such as HashSet, HashMap, and Hashtable.

Why do you say that? Consider the case of how to determine if an object already exists in the collection when it is inserted into the collection. (Note: Duplicate elements in the collection are not allowed to exist)

Perhaps most people would have thought of calling the Equals method to make comparisons on a case by case basis. However, if there are already 10,000 or more data in the collection, if the Equals method is used to compare each other, efficiency must be a problem. At this point the function of the Hashcode method is reflected, when the collection to add a new object, the first call the object's Hashcode method, to get the corresponding hashcode value, In fact, in the implementation of HASHMAP will use a table to save the hashcode value of the object that has been stored in, if the table does not have the hashcode value, it can be stored directly in, no further comparison, if there is a hashcode value, The Equals method that calls it is compared with the new element, the same is not saved, and the other addresses are hashed, so there is a conflict resolution problem, so the number of actual calls to the Equals method is greatly reduced. The Hashcode method in Java is to map the object-related information (such as the object's storage address, the object's field, and so on) into a numeric value, which is called a hash, according to certain rules. The following code is a concrete implementation of the Put method in Java.util.HashMap:

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

The Put method is used to add a new element to the HashMap, and from the concrete implementation of the Put method, the Hashcode method is called to get the hashcode value of the element, and then see if the hashcode value exists in the table. If present, call the Equals method to re-determine if the element exists, update the value if it exists, or add the new element to HashMap. As can be seen here, the Hashcode method exists to reduce the number of calls to the Equals method, thus improving program efficiency.

If the hash table of the data structure of the friend is not clear, you can refer to these blog posts;

Http://www.cnblogs.com/jiewei915/archive/2010/08/09/1796042.html

Http://www.cnblogs.com/dolphin0520/archive/2012/09/28/2700000.html

http://www.java3z.com/cwbwebhome/article/article8/83560.html?id=4649

Some friends mistakenly think that by default, Hashcode returns the object's storage address, in fact, this view is not comprehensive, indeed some JVMs are implemented to directly return the object's storage address, but most of the time is not the case, can only say that the storage address has a certain association. Here is the implementation of the hash hash value generated in the hotspot JVM:

Static inline intptr_t Get_next_hash (Thread * self, oop obj) {intptr_t value = 0;  if (hashcode = = 0) {//This form uses a unguarded global park-miller RNG,//So it's possible for the threads to     Race and generate the same RNG.     On MP system we'll have lots for RW access to a global, so the//mechanism induces lots of coherency traffic.  Value = Os::random (); } else if (hashcode = = 1) {//This variation have the property of being stable (idempotent)//between STW Operat  Ions.     This can is useful in some of the 1-0//synchronization schemes.     intptr_t addrbits = intptr_t (obj) >> 3;  Value = addrbits ^ (addrbits >> 5) ^ gvars.stwrandom;            } else if (hashcode = = 2) {value = 1;  For sensitivity testing} else if (hashcode = = 3) {value = ++gvars.hcsequence;  } else if (hashcode = = 4) {value = intptr_t (obj); } else {//Marsaglia ' s xor-shift scheme with thread-specific state//This is PRObably the best overall implementation--we'll/likely make this the default in future releases.     unsigned t = Self->_hashstatex;     T ^= (t << 11);     Self->_hashstatex = Self->_hashstatey;     Self->_hashstatey = Self->_hashstatez;     Self->_hashstatez = Self->_hashstatew;     Unsigned v = self->_hashstatew;     v = (v ^ (v >>)) ^ (t ^ (T >> 8));     Self->_hashstatew = v;  Value = V;  } value &= Markoopdesc::hash_mask;  if (value = = 0) value = 0xBAD;  ASSERT (Value! = Markoopdesc::no_hash, "invariant");  Tevent (hashcode:generate); return value;}

The implementation is located under the Hotspot/src/share/vm/runtime/synchronizer.cpp file.

  So some people would say that two objects can be judged directly according to the hashcode value of equality? It is certainly not possible, because different objects may generate the same hashcode value. Although it is not possible to determine whether two objects are equal according to the Hashcode value, two objects can be judged directly from the hashcode value, and if the hashcode value of two objects is unequal, it must be two different objects. If you want to determine whether two objects are truly equal, you must pass the Equals method.

That is to say, for two objects, if the result of calling the Equals method is true, then the hashcode value of the two objects must be equal;

If the Equals method results in False, the hashcode value of the two objects may not be different;

If the hashcode value of two objects is not equal, the result of the Equals method must be false;

If the hashcode value of two objects is equal, the result of the Equals method is unknown.

Two. Equals Method and Hashcode method

In some cases, the programmer needs to rewrite the Equals method when designing a class, such as the string class, but it is important to note that the Hashcode method must be overridden while overriding the Equals method . Why do you say that?

Let's look at an example:

Package Com.cxh.test1;import Java.util.hashmap;import Java.util.hashset;import java.util.set;class People{private string Name;private int age;public people (String Name,int age) {this.name = Name;this.age = age;} public void Setage (int.) {this.age = age;} @Overridepublic boolean equals (Object obj) {//TODO auto-generated method Stubreturn this.name.equals (((people) obj). Name) && this.age== ((people) obj). Age;}} public class Main {public static void main (string[] args) {people p1 = new People ("Jack", 12); System.out.println (P1.hashcode ()); Hashmap<people, integer> HashMap = new hashmap<people, integer> (); Hashmap.put (P1, 1); System.out.println (Hashmap.get (New People ("Jack", 12));}}

Here I only rewrite the Equals method, and say if the two people object, if its name and age are equal, it is considered the same person.

The intent of this code is to think that the output of this code is "1", but in fact it outputs "null". Why is it? the reason is to rewrite the Equals method while forgetting to rewrite the Hashcode method.

Although the two objects with the same logical name and age are judged to be equal objects by overriding the Equals method (similar to the String Class), it is known by default that the Hashcode method maps the storage address of an object. It is not surprising that the output of the above code is "null". The reason is simple, P1 points to the object and

System.out.println (Hashmap.get ("Jack", 12)), the new people in this sentence ("Jack", 12) produces two objects whose storage address must be different. The following is a concrete implementation of the Get method for HashMap:

Public V get (Object key) {        if (key = = null)            return Getfornullkey ();        int hash = hash (Key.hashcode ());        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;    }

So when HashMap get operations, because the resulting hashcdoe value is different (note that the above code may in some cases get the same hashcode value, but this probability is relatively small, Because although the storage address of two objects is different, it is possible to get the same hashcode value), so the for loop in the Get method does not execute and returns null directly.

So if you want the above code to output as "1", it is very simple to rewrite the Hashcode method so that the equals method and the Hashcode method always logically maintain consistency .

Package Com.cxh.test1;import Java.util.hashmap;import Java.util.hashset;import java.util.set;class People{private string Name;private int age;public people (String Name,int age) {this.name = Name;this.age = age;} public void Setage (int.) {this.age = age;} @Overridepublic int hashcode () {//TODO auto-generated method Stubreturn Name.hashcode () *37+age;} @Overridepublic boolean equals (Object obj) {//TODO auto-generated method Stubreturn this.name.equals (((people) obj). Name) && this.age== ((people) obj). Age;}} public class Main {public static void main (string[] args) {people p1 = new People ("Jack", 12); System.out.println (P1.hashcode ()); Hashmap<people, integer> HashMap = new hashmap<people, integer> (); Hashmap.put (P1, 1); System.out.println (Hashmap.get (New People ("Jack", 12));}}

As a result, the output will be "1".

This passage is excerpted from the effective Java book:

    • The Hashcode method must consistently return the same integer as long as the information used for the comparison operation of the Equals method is not modified during program execution, and the same object is called multiple times.
    • If two objects are equal according to the Equals method, then the Hashcode method that calls two objects must return the same integer result.
    • If two objects are unequal according to the Equals method, the Hashcode method does not necessarily return different integers.

Good understanding of the second and third articles, but the first one is often overlooked. The P495 page in the Java Programming Idea book also has a similar paragraph to the first one:

The most important factor in designing hashcode () is that whenever you call Hashcode () on the same object, you should produce the same value. If a Hashcdoe value is generated when an object is added to the HashMap with put (), and a Get () is taken out with another hashcode value, then the object cannot be obtained. So if your Hashcode method relies on variable data in the object, the user should be careful because the hashcode () method generates a different hash code when this data changes .

Here's an example:

  

Package Com.cxh.test1;import Java.util.hashmap;import Java.util.hashset;import java.util.set;class People{private string Name;private int age;public people (String Name,int age) {this.name = Name;this.age = age;} public void Setage (int.) {this.age = age;} @Overridepublic int hashcode () {//TODO auto-generated method Stubreturn Name.hashcode () *37+age;} @Overridepublic boolean equals (Object obj) {//TODO auto-generated method Stubreturn this.name.equals (((people) obj). Name) && this.age== ((people) obj). Age;}} public class Main {public static void main (string[] args) {people p1 = new People ("Jack", 12); System.out.println (P1.hashcode ()); Hashmap<people, integer> HashMap = new hashmap<people, integer> (), Hashmap.put (P1, 1);p 1.setAge (13); System.out.println (Hashmap.get (p1));}}

The result of this code output is "null", presumably for the reason that everyone should be clear.

Therefore, when designing the Hashcode method and the Equals method, it is best not to rely on the field in the Equals method and the Hashcode method if the data in the object is variable.

Talking about the Hashcode method in Java

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.