Why Do We Have to Override the Equals() Method in Java

Why do I need to override the equals and hashCode methods in Java?

Joshua Bloch says on Effective Java

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Let's try to understand it with an example of what would happen if we override equals() without overriding hashCode() and attempt to use a Map.

Say we have a class like this and that two objects of MyClass are equal if their importantField is equal (with hashCode() and equals() generated by eclipse)

public class MyClass {
private final String importantField;
private final String anotherField;

public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}

@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}

Imagine you have this

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Override only equals

If only equals is overriden, then when you call myMap.put(first,someValue) first will hash to some bucket and when you call myMap.put(second,someOtherValue) it will hash to some other bucket (as they have a different hashCode). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.


Although it is not necessary to override equals() if we override hashCode(), let's see what would happen in this particular case where we know that two objects of MyClass are equal if their importantField is equal but we do not override equals().

Override only hashCode

If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).

But the problem is that equals was not redefined, so when the map hashes second and iterates through the bucket looking if there is an object k such that second.equals(k) is true it won't find any as second.equals(first) will be false.

Hope it was clear

Why override equals instead of using another method name

This seems like a silly question but why do we override equals method instead of creating a new method with new name and compare using it?

Because all standard collections (ArrayList, LinkedList, HashSet, HashMap, ...) use equals when deciding if two objects are equal.

If you invent a new method these collections wouldn't know about it and not work as intended.

The following is very important to understand: If a collection such as ArrayList calls Object.equals this call will, in runtime, resolve to the overridden method. So even though you invent classes that the collections are not aware of, they can still invoke methods, such as equals, on those classes.

If I didn't override equals that means both == and equals check whether both references are pointed to same memory location?

Yes. The implementation of Object.equals just performs a == check.

Overriding the equals() method

When overriding equals try the following. It is necessary to check for null values and improper types to prevent NPE's and casting exceptions.

  class Circle{
double radius;

@Override
public int hashCode() {
return Double.valueOf(radius).hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Circle) {
Circle circle = (Circle)obj;
return this.radius == circle.radius;
}
return false;
}
}

The main point to remember is that the type of the equals method is Object, not your class type.

Why does Java's Area#equals method not override Object#equals?

RealSkeptic linked to JDK-4391558 in a comment above. The comment in that bug explains the reasoning:

The problem with overriding equals(Object) is that you must also
override hashCode() to return a value which guarantees that equals()
is true only if the hashcodes of the two objects are also equal.

but:

The problem here is that Area.equals(Area) does not perform a very
straight-forward comparison. It painstakingly examines each and every
piece of geometry in the two Areas and tests to see if they cover the
same enclosed spaces. Two Area objects could have a completely
different description of the same enclosed space and equals(Area)
would detect that they were the same.

So basically we're left with an array of not-so-pleasant options, such as:

deprecate equals(Area) and create an alternate name for that
operation, such as "areasEqual" so as to avoid the confusion.
Unfortunately, the old method would remain and would be linkable and
would trap many people who were intending to invoke the equals(Object)
version.

or:

deprecate equals(Area) and change its implementation to be exactly
that of equals(Object) so as to avoid semantic problems if the wrong
method is called. Create a new method with a different name to avoid
confusion to implement the old functionality provided by equals(Area).

or:

implement equals(Object) to call equals(Area) and implement a dummy
hashCode() which honors the equals/hashCode contract in a degenerate
way by returning a constant. This would make the hashCode method
essentially useless and make Area objects nearly useless as keys in a
HashMap or Hashtable.

or other ways to modify the equals(Area) behavior that would either change its semantics or make it inconsistent with hashCode.

Looks like changing this method is deemed by the maintainers to be neither feasible (because neither option outlined in the bug comment quite solves the problem) nor important (since the method, as implemented, is quite slow and would probably only ever return true when comparing an instance of an Area with itself, as the commenter suggests).

What is the need to override equals method in String class?

Not all "String in Java are interned". Try reading something from a file or console, those Strings aren't "interned", thus equals() (and also hashcode()) needs to be overriden.

when should I override Equals function?

should I override equals function for any class that I create?

Override equals if (and only if) the object "represents some data", i.e. if it models something such as Person, Car or RecipieIngredient (these typically end up in collections etc). Don't override equals for other types of classes, for example LoginServlet or DatabaseUtil.

Remember to always override hashCode whenever you override equals.

(A natural follow-up question:) What happens if I don't override equals and hashCode?

Any two objects will be considered unequal unless they are the exact same object.

[...] I need every single attribute of it to be equal?

Typically yes. It depends on how you define your notion of equality. Note that for reference types, you can reuse/delegate to that objects implementation of equals (and hashCode) when implementing your own.

Related questions:

  • why we need to override equals and hashcode in java and why cannot we use Object class implementation
  • Why do I need to override the equals and hashCode methods in Java?

When should I override the equal method in Java

if you really want to use equals method than your comparison should be like this :-

public boolean checkISBN(Book b){
boolean isbnExists = false;
for (Book bList : bookList)
{
if(**bList.equals(b)**){
System.out.println("Book ISBN already exist");
isbnExists = true;
}
}
return isbnExists;
}

Notice highligheted part.

your lecturer wants to do this to make understant the usage of equals method.
let me explain you difference between your approach and his approch. Then advantage of doing this :-
1.Without using equals :- In this approach , your comparing the ISBN of book, and you are not putting book having same ISBN in shelf.


  1. With equals overriding :-
    First , you are overriding equals, which means you are saying two book having same ISBN are equal. Step two you comparing if books are equal.

So what is advantage , let say you need to other operation based on same equality also in your program , then you will not end up writing ISBN logic everywhere , you just need to write equals.
Advantage 1 :- In real world, differnt parts of software are written by different persons, so if you are writting book , other person will not need to know what makes two book equal. his part of design or requirment, will be two write code based on equality.

Advantage 2 :- Tommarrow, you decide that equality of book is not only based on ISBN no. but also author , you only need to update equals method and everyones code will work fine no need to change everyone.

In real programming world, object oriented coding done for real objects and sometimes object changes due to businees channges in real world

How to override equals method in Java

//Written by K@stackoverflow
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));

for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));

System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}

//written by K@stackoverflow
public class Person {
private String name;
private int age;

public Person(String name, int age){
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}

if (obj.getClass() != this.getClass()) {
return false;
}

final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}

if (this.age != other.age) {
return false;
}

return true;
}

@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

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

Output:

run:

-- Subash Adhikari - VS - K false

-- Subash Adhikari - VS - StackOverflow false

-- Subash Adhikari - VS - Subash Adhikari true

-- K - VS - StackOverflow false

-- K - VS - Subash Adhikari false

-- StackOverflow - VS - Subash Adhikari false

-- BUILD SUCCESSFUL (total time: 0 seconds)



Related Topics



Leave a reply



Submit