Look at the phenomenon

Public class TestDemo {public static void main(String[] args) {Person p1 = new Person(" args "); Person p2 = new Person(" Person "); System.out.println(p1.equals(p2)); } static class Person { public Person(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() ! = o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); }}}Copy the code

After the above code runs, you can see that the output is true.

As you can see, if you override equals (), two objects are considered to be the same as long as they have the same type and the same name, instead of using the default memory address to check whether they are the same.

In our business code, we would need to do this. For example, if a person’s name is equal, it means the same person. Normally, if we overwrite equals, it’s enough.

Why rewrite hashCode

Instead of rewriting hashCode based on the code in the beginning, let’s make a requirement to filter out duplicate people, such as two people named Aron, but let’s save only one,

So let’s use HashMap, because HashMap’s keys are unique and unduplicate. We’ve overridden Person’s equals method, so as long as the names are the same object, HashMap will recognize and unduplicate it. Let’s take a look at the results:

Public static void main(String[] args) {Person p1 = new Person(" args "); Person p2 = new Person(" Person "); Map<Person, String> map = new HashMap<>(); map.put(p1, p1.getName()); map.put(p2, p2.getName()); System.out.println("map length :" + map.size()); map.forEach((key, value) -> { System.out.println(key.getName()); }); }Copy the code

Isn’t it amazing that there is no de-weighting, and that both are saved in the HashMap, which does not achieve the desired effect? Why?

First, the internal memory of a HashMap is an array, but the lookup speed of a HashMap is very fast, basically O(1).

This is because it uses a Hash algorithm, a HashMap that hashes the key to produce an integer value, which is the subscript to be stored in the final storage array. Of course, there is a series of processing for this part, which you can refer to related materials for further understanding. Here is just a brief description.

We did not override the hashCode () method to compare equals to the name of the two objects, but we used hashCode to calculate a value and store it under the corresponding array index.

The values p1 and P2 in the above code return different hashCode values, so the calculated subscripts are different, causing them to be stored under different array subscripts in the HashMap.

In the figure above, the two objects in the heap address have the same name, but the hashCode is different. If we compare them by name and hash them, then they are equal. But HashMap uses hashCode, so we need to rewrite the hashCode method. The same meaning applies to hashCode belonging to an object at the business level.

In the following figure, we can change hashCode to name:

Implementation code:

Public class TestDemo {public static void main(String[] args) {Person p1 = new Person(" args "); Person p2 = new Person(" Person "); System.out.println(p1.hashCode()); System.out.println(p2.hashCode()); Map<Person, String> map = new HashMap<>(); map.put(p1, p1.getName()); map.put(p2, p2.getName()); map.get(p1); System.out.println("map length :" + map.size()); map.forEach((key, value) -> { System.out.println(key.getName()); }); } static class Person { public Person(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() ! = o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name); }}}Copy the code

As you can see, when we overwrite hashCode to use the name of the object as the computed value to produce the final hash value, HashMap allows us to route two objects to a subscript and then check with equals to determine that they are the same object.

conclusion

After rewriting equals based on the business situation, be sure to hash hashCode using the same rules to prevent misunderstandings and problems when using hashCode objects.

It may not be the same object, but a hashCode is the same. How does a HashMap handle that?

If two objects have a hashCode conflict, they will be stored in a linked list under the same subscript. When obtaining the object, they will be routed to the corresponding location through hashCode. Then, the list will be looped through to equals method for comparison, and the final correct value will be returned.

Rewrite the equals and hashCode principles:

1. Reflexivity: x.equals(x) == true, self and self are more equal

2. Symmetry: x.equals(y) == y.equals(x), two objects should have the same result when calling equals

3. Transitivity: if x.equals(y) == true y.equals(z) == true, then x.equals(z) == true,x equals y, y equals z, then x equals z

4. Consistency: If x and y have member variables num1 and num2, and the overridden equals method only num1 participates, then changing num2 does not affect x.equals(y)