overriding equals in Java be sure to rewrite the Hashcode method
Original Author Blog address: Working with hashcode () and Equals () equals and Hashcode method equals (Object obj): Provided by the object class, Used to determine whether the current object and obj are equal. The default implementation in JDK is based on memory address: Only two objects have equal memory addresses, they are equal hashcode (): The method is provided by the object class, and returns an integer value representing the address of the object in memory, for each object, Hashcode () method to randomly return a unique integer value using the rule if X.equals (y) ==true, then X.hashcode () ==y.hashcode () must also return True
In other words, if you override the Equals method in a class, you must override the Hashcode method
Entity class Ball.java:
public class Ball {
private int id;
private String color;
public Ball (int ID, String color) {
this.id = ID;
This.color = color;
}
public int getId () {return
ID;
}
public void setId (int id) {
this.id = ID;
}
Public String GetColor () {return
color;
}
public void SetColor (String color) {
This.color = color;
}
}
Scenario 1: Equals and Hashcode methods that directly use the object default implementation
Test:
@Test public
void Commonequals () {
Ball ball1 = new Ball (1, "red");
Ball ball2 = new Ball (1, "red");
System.out.println ("Ball1 hashcode:" + ball1.hashcode ());
System.out.println ("Ball2 hashcode:" + ball2.hashcode ());
System.out.println ("Ball1.equals (BALL2):" + ball1.equals (ball2));
Results:
Ball1 hashcode:26947503
ball2 hashcode:22527820
ball1.equals (BALL2): false
Obviously, different objects, hashcode unequal, ball1 and ball2 naturally unequal . Scenario 2: Overriding the Equals method without overriding the Hashcode method
To override the Equals method:
@Override public
boolean equals (Object obj) {
if (this = = obj) return
true;
if (obj = = NULL | | getclass ()!= Obj.getclass ()) return
false;
Ball that = (Ball) obj;
return Id==that.getid ();
}
Results:
Ball1 hashcode:30430942
ball2 hashcode:16191201
ball1.equals (ball2): True
In the Equals method, we use IDs as equal conditions for two objects
If you use ball after you have overridden only equals in ArrayList
@Test public
void Equalswitharraylist () {
arraylist<ball> ArrayList = new arraylist<> ();
Ball ball1 = new Ball (1, "red");
Ball ball2 = new Ball (1, "red");
Arraylist.add (BALL1);
Arraylist.add (BALL2);
System.out.println ("ArrayList size:" + arraylist.size ());
System.out.println ("arrayList contains Ball:" + arraylist.contains (new Ball (1, "red"));
}
As if nothing had been found, the ArrayList would have allowed repeating elements
If you use the ball after the rewrite in HashMap
@Test public
void Equalswithhashmap () {
hashmap<ball, integer> map = new hashmap<> ();
Ball ball1 = new Ball (1, "red");
Ball ball2 = new Ball (1, "red");
Map.put (BALL1, 1);
Map.put (ball2,1);
SYSTEM.OUT.PRINTLN ("Map size:" + map.size ());
SYSTEM.OUT.PRINTLN ("map contains Ball:" + map.get (new Ball (1, "red"));
}
Results:
Map Size:2
map contains Ball:null
To be reasonable, map size should be 1, and Map.get (new Ball (1, "red") should return 1. Find HashMap's put source:
Public V-Put (K key, V value) {
if (table = = empty_table) {
inflatetable (threshold);
}
if (key = = null) return
Putfornullkey (value);
int hash = hash (key);
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;
}
All right, HashMap's put operation is to compare the equality of hashcode values, and then compare the Equals method, which is considered addentry if the hashcode is not equal. In fact, Ball did not rewrite the Hashcode method, the first new operation, will be allocated in memory different addresses, so map.size=2.
Look again, Map.get () source method:
Hashmap.get () source code public
V get (Object key) {
if (key = null) return
getfornullkey ();
entry<k,v> Entry = Getentry (key);
return NULL = = entry? Null:entry.getValue ();
}
Final entry<k,v> getentry (Object key) {
if (size = = 0) {return
null;
}
int hash = (key = null)? 0:hash (key);
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!= null && key.equals (k)
)) return e;
}
return null;
}
You can see that when you take a value, you first compute the object's hashcode value, Map.get (New Ball (1, "red"), the anonymous new Ball (1, "Red"), and the Hashcode method is not rewritten before, and a new hashcode is generated. So the null rewrite Hashcode method is taken out
@Override public
int hashcode () {return
ID;
}
Run the Equalswithhashmap test case again, and the result:
Map size:1
map contains ball:1
As you can see, the size of this map is 1, indicating that the BALL1 and Ball2 joined are the same instance
It also applies to hashset,hashtable, or other data structures that use the hashcode mechanism as storage
conclusion: After overriding the Equals method, it is mandatory to override the Hashcode method if two objects are equal, then they must have the same hashcode if two objects have the same hashcode, it does not mean they are equal Overriding the Equals method alone will invalidate data in your business that uses hash data structures, such as hashmap,hashset,hashtable ...