Understanding hashCode and equals methods in Java



All classes in Java directly or indirectly inherit the java.lang.Object class. The Object class provides 11 methods, as follows:
````
1,clone()
2,equals(Object obj)
3,finalize()
4,getClass()
5,hashCode()
6,notify()
7,notifyAll()
8,toString()
9,wait()
10,wait(long timeout)
11,wait(long timeout, int nanos)
````


There are three commonly used methods here:

````
toString()
equals(Object obj)
hashCode()
````



The toString method is believed to be familiar to anyone who has used Java. The default print is: class name@hexadecimal hashCode, which is defined in the source code as follows:
````
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
````
After rewriting, we can print all attributes of a class, which is convenient when printing log or debugging.

The following focuses on the hashCode and equals methods:



(1) The equals method compares the memory address of the object by default in the JDK. The source code is as follows:
````
    public boolean equals(Object obj) {
        return (this == obj);
    }
````


(2) The hashcode method returns a unique integer by default, representing the memory address of the instance. Note that this number is
not the actual memory address. Java cannot directly obtain the memory address. It must be determined by C or C++. Get, so this method is
modified with native
````
public native int hashCode();
````




Because by default, the equals method compares the memory address, and in actual development, we judge whether two objects are equal, generally based on the attributes of the object,
so we need to rewrite this method, otherwise, there is no way to compare. An example is as follows:


The defined class is as follows:
````
public class Hero {
    private String id;
    private String name;

    public Hero() {
    }

    public Hero(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}
````
Comparing two objects directly, the results are not equal:
````
 ``````  Hero h1=new Hero("1","张飞");

        Hero h2=new Hero("1","张飞");

        //false
        System.out.println(h1.equals(h2));
````



Because their memory addresses are different, the result is false. If we want to consider them equal, we need to override the
equals method:
````
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;//If the memory addresses are equal, the two objects must be equal
        if (o == null || getClass() != o.getClass()) return false;//If one is null or the class is different, it is considered not equal

        Hero hero = (Hero) o;//

        if (id != null ? !id.equals(hero.id) : hero.id != null) return false;//Compare id, return false if they are not equal
        return name != null ? name.equals(hero.name) : hero.name == null;//After the above conditions are passed, compare the names, if they are equal, return true, if they are not equal, return false

    }

````


After overriding the equals method, we compare the two objects and find that they are equal
````
```     Hero h1=new Hero("1","张飞");

        Hero h2=new Hero("1","张飞");

        //true
        System.out.println(h1.equals(h2));
````


Then we look at the second example, put it into the ArrayList, and then judge whether it exists, and find that it also takes effect:
````
`        Hero h1=new Hero("1","张飞");

        List<Hero> heros=new ArrayList<Hero>();

        heros.add(h1);

        //true
        System.out.println(heros.contains(new Hero("1","张飞")));
````


So far, we have not operated on hashCode, so you may have a question, since there are equals methods for comparison, why do you need the hashCode method? Don't worry, continue to look at the following example:

We all know that the HashSet class in Java can be deduplicated out of order. Let's see if only rewriting the equasl method can achieve deduplication of the class:
````

`        Hero h1=new Hero("1","张飞");

        Hero h2=new Hero("1","张飞");

        Set<Hero> heros=new HashSet<Hero>();
        heros.add(h1);
        heros.add(h2);

        //2
        System.out.println(heros.size());
        //false
        System.out.println(heros.contains(new Hero("1","张飞")));
````



From the above results, there is no deduplication. Some friends will say why can deduplication be possible when the string type is used? This is because the Stirng class has overridden the equals and hashcode methods by default, and of course all basic types override these two methods.

Then go back to the above question, why does deduplication fail in HashSet?


In fact, not only HashSet, but also in all hash-related data structures such as HashMap and Hashtable, if the hashcode is not rewritten when used, then there is no way to compare whether the object exists.


This is actually related to the storage principle of HashMap (HashMap is also used at the bottom of HashSet). HashMap actually uses the storage structure of array + linked list when storing. Each element
in the array can be understood as a bucket (bucket), bucket The structure inside is a linked list, and how the data is divided into buckets has a lot to do with the hashCode. Only
objects can be divided into a bucket. When saving, traverse the linked list to determine whether it exists. If it exists, it will not be overwritten. If it does not exist, put the element in the head of the linked list, point next to the previous head, and when reading, locate the bucket first, and then traverse
The linked list can find the element.



After understanding this, I understand why in the above example, deduplication fails. It is because their hashCodes are different that they are divided into different buckets, and naturally they cannot be deduplicated.

After rewriting hashCode, look at the result:
````
    @Override
    public int hashCode() {
        return  Integer.parseInt(id);
    }
````


````
·        Hero h1=new Hero("1","张飞");

        Hero h2=new Hero("1","张飞");

        Set<Hero> heros=new HashSet<Hero>();
        heros.add(h1);
        heros.add(h2);

        //1
        System.out.println(heros.size());
        //true
        System.out.println(heros.contains(new Hero("1","张飞")));
````


This time the result is correct.


So the question is, why do you need hashCode? Because in HashSet, a large number of elements can be stored. If there is no hashCode, then each element must be compared in full to determine
whether it exists, so the efficiency must be extremely low, and after hashCode, only need to find the data The linked list, and then traverse the data of this linked list, so that the efficiency
is greatly improved.


Summary:

(1) If two objects are equal, then they must have the same hashcode

(2) If the hashcodes of two objects are equal, they are not necessarily equal

(3) When rewriting the equalsl method, be sure to remember to rewrite the hashcode method , especially used in hash-like data structures.



If you have any questions, you can scan the code and follow the WeChat public account: I am the siege division (woshigcs), leave a message in the background for consultation. Technical debts cannot be owed, and health debts cannot be owed. On the road of seeking the Tao, walk with you.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326347910&siteId=291194637