Comparing objects

Module 1. Java Syntax
Level 13 , Lesson 4
Available

1. Comparing objects in Java

In Java, objects can be compared both by reference and by value.

Comparing references

If two variables point to the same object in memory, then the references stored in these variables are equal. If you compare these variables using the equality operator (==), you get true, and that result makes sense. Everything is simple here.

Code Console output
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Comparing by value

But you can often encounter situations where two variables refer to two distinct objects that are identical. For example, two different strings objects that contain the same text.

To determine whether different objects are identical, use the equals() method. For example:

Code Console output
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

The equals method is not limited to the String class. Every class has it.

Even classes that you write on your own, and here's why.



2. Object class

All classes in Java inherit the Object class. Java's creators came up with this approach.

And if a class inherits the Object class, then it gains all the methods of the Object class. And this is a major consequence of inheritance.

In other words, every class has the methods of the Object class, even if their code does not mention them.

These inherited methods include methods related to object comparison. These are the equals() and hashCode() methods.

Code In reality, here's what we'll have:
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

In the example above, we created a simple Person class with name and age parameters, but not a single method. But because all classes inherit the Object class, the Person class automatically has two methods:

Method Description
boolean equals(Object obj)
Compares the current object and the passed object
int hashCode()
Returns the hashcode of the current object

It turns out that absolutely every object has the equals method, and objects of different types can be compared with each other. Such code will compile and work perfectly.

Code Console output
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals() method

The equals() method, inherited from the Object class, implements the simplest algorithm for comparing the current object with passed objects: it just compares references to the objects.

You get the same result if you just compare Person variables instead of calling the equals() method. Example:

Code Console output
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

When the equals method is called on a, it simply compares the reference stored in the a variable with the reference stored in the b variable.

However, comparison works differently for the String class. Why?

Because the folks who created the String class wrote their own implementation of the equals() method.

Implementation of the equals() method

Now let's write our own implementation of the equals method in the Person class. We'll consider 4 main cases.

Important:
Regardless of which class overrides the equals method, it always takes an Object object as an argument

Scenario 1: the same object on which the equals method is called is also passed to the equals method. If the references of the current object and the passed object are equal, the method must return true. An object is equal to itself.

In code it will look like this:

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Compare references

Scenario 2: null is passed to the equals method — we have nothing to compare to. The object on which the equals method is called is definitely not null, so we need to return false in this case.

In code it will look like this:

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Compare references


Is the passed object null?

Scenario 3: a reference to an object that is not a Person is passed to the equals method. Is the Person object equal to the non-Person object? That is a question for the developer of the Person class to decide however he or she wants.

But usually objects must be of the same class to be considered equal. Therefore, if something other than an object of the Person class is passed to our equals method, then we will always return false. How can you check the type of an object? That's right — by using the instanceof operator.

Here's what our new code looks like:

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Compare references


Is the passed object null?


If the passed object is not a Person

4. Comparing two Person objects

What did we end up with? If we have reached the end of the method, then we have a Person object reference that is not null. So we convert it to a Person and compare the relevant internal data of both objects. And that is our fourth scenario.

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Compare references


Is the passed object null?


If the passed object is not a Person


Typecasting

And how do you compare two Person objects? They are equal if they have the same name (name) and age (age). The final code will look like this:

Code Description
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Compare references


Is the passed object null?


If the passed object is not a Person


Typecasting

But that's not all.

First, the name field is a String, so you need to compare the name field by calling the equals method.

this.name.equals(person.name)

Second, the name field may be null: in that case, you cannot call equals on it. You need an additional check for null:

this.name != null && this.name.equals(person.name)

That said, if the name field is null in both Person objects, then the names are still equal.

The code for the fourth scenario might look like this:

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


If the ages are not equal,
immediately return false

If this.name is equal to null, there is no point in comparing using the equals method. Here either the second name field is equal to null, or it is not.

Compare the two name fields using the equals method.


5. hashCode() method

In addition to the equals method, which is intended to perform a detailed comparison of all the fields of both objects, there is another method that can be used for an imprecise but very quick comparison: hashCode().

Imagine you are alphabetically sorting a list of thousands of words, and you need to repeatedly compare pairs of words. And the words are long, consisting of lots of letters. Generally speaking, such a comparison would take a very long time.

But it can be accelerated. Suppose we have words that begin with different letters — it's immediately clear that they are different. But if they begin with the same letters, then we can't yet say what the result will be: the words may turn out to be equal or different.

The hashCode() method works using a similar principle. If you call it on an object, it returns some number — analogous to the first letter in a word. This number has the following properties:

  • Identical objects always have the same hashcode
  • Different objects can have the same hashcode, or their hashcodes can be different
  • If objects have different hashcodes, then the objects are definitely different

To make this even more clear, let's reframe these properties in terms of words:

  • Identical words always have the same first letters.
  • Different words can have the same first letters, or their first letters can be different
  • If words have different first letters, then the words are definitely different

The last property is used to accelerate comparison of objects:

First, the hashcodes of the two objects are calculated. If these hashcodes are different, then the objects are definitely different, and there is no need to compare them further.

But if the hashcodes are the same, then we still have to compare the objects using the equals method.



6. Contracts in code

The behavior described above must be implemented by all classes in Java. During compilation, there is no way to check whether objects are compared correctly.

Java programmers have a universal agreement that if they write their own implementation of the equals() method and thereby override the standard implementation (in the Object class), they must also write their own implementation of the hashCode() method in such a way that the aforementioned rules are satisfied.

This arrangement is called a contract.

If you implement only the equals() or only the hashCode() method in your class, then you are in gross violation of the contract (you've broken the agreement). Don't do this.

If other programmers use your code, it may not work correctly. What's more, you will use code that relies on adherence to the above contracts.

Important!

When searching for an element, all Java collections first compare the hashcodes of objects, and only then perform a comparison using the equals method.

That means that if you give your own class an equals method but you do not write your own hashCode() method or you implement it incorrectly, then collections may not work correctly with your objects.

For example, you might add an object to a list and then search for it using the contains() method, but the collection might not find your object.

Comments (17)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Evgeniia Shabaeva Level 32, Budapest, Hungary
29 April 2024
When I was doing the task in the 4th section, I was initially scared by the task to override a method (as we had not yet studied this whole overriding thing). But then I just wrote my own method called public boolean equals(Object obj) (almost the same as in the text of the lesson, just with different names in it) under the comment //write your code here, and it worked perfectly well. So, the good news is we do not need @Override stuff right now (though I hope we'll cover it soon).
Pekotski Level 16, Zurich, Switzerland
2 December 2023
Two Iphones - beyond my understanding why is this allowed at this stage? Inheritance or overwriting was not covered yet. This course is becoming a joke.
Tomasz Enyeart Level 16, United States
9 February 2024
i dpnt want to say it but this cousre kinda sucks, and they dont care, half the tasks have requirement like "follow the description " and then they do this
Campbell, Jackson Level 12, Corona, USA
6 November 2023
idk how I did it right ngl
Harshit Garg Level 23, Chandigarh, India Expert
20 August 2023

class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}
what does "this== obj" do? what does @override do? what is hashcode -- what does "Objects.hash(model, year);" do?
Serhii Matviienko Level 21, Ukraine, Ukraine Expert
20 August 2023
@Override annotation informs the compiler that the element is meant to override an element declared in a superclass. It's not always required, but helps to prevent errors. Since every object in Java extends Object class, we usually override it's equals(Object obj) and hashCode() methods. And if we override parent's method and use @Override and then decide to change method's signature (like add some paramether to method or change it's name), compiler will generate an error, code won't compile.
Parsa Level 62, Bangalore, India Expert
19 November 2023
this == obj compares their references. If both this and obj point to the same object, then they're both equal. Here's a photo I made. I hope it helps: And regarding the hashCode method, it returns an integer which is the hash code. The hash code is internally calculated based on the objects given, here, model and year. If model and year are the same, the hash code will also be the same, so we can use that for comparison.
curi0usmind Level 10, Switzerland Expert
10 August 2023
"That said, if the name field is null in both Person objects, then the names are still equal." How? return this.name != null && this.name.equals(that.name) && this.age == that.age; If both are null then the condition this.name != null makes the whole return statement return false. And when one of them is null then this.name.equals(that.name) will return false. So it's not possible for the names to still be equal!
Zac Level 17, Austin, United States
3 June 2023
The problem with the answers below saying things like, "just search the internet!" Is that this course never suggests you're supposed to. The course, so far, has clearly implied that it is teaching you the skills and knowledge you need to answer the questions. It's totally great and normal if we need to jump on google or stack overflow to get answers -- that itself is a core skill we all need! But if the course is suggesting that it has equipped us to solve an exercise when that isn't true, then the course should at minimum say something like, "We haven't covered Overriding yet -- do a google search to see how this is implemented" or similar.
Abe Level 13, los angeles, United States
20 February 2023
I have no clue how to even understand the solution for the 'two iphones' task: what is the 'getClass()' doing? for instance...but really I wish there was a line by line explanation describing what it is doing...
Gandhar K Level 37, India Expert
3 October 2023
getClass() is the method of object class. Its used to get the class of that object. Basically the line checks if objects in comparison belong to same classes. Objects of different classes cannot be considered equal.
Dillon Morgan Level 21, London, United Kingdom
13 February 2023
This lesson is one of the more difficult to follow. I feel the use of the Integer object for the examples earlier on in the lesson can make it difficult to see the difference between different implementations of the inherited "equals" methods. Especially when the String class is identified as having a special implementation but Integer class is not. Note the conflicting messages at the end of Section 2 and beginning of Section 3.
Patryk Kotański Level 24, Poland
23 December 2022
How am I supposed to write my own implementation of a function that I haven't used regular one before? This task teaches nothing.
17 January 2023
Well... it teaches you to search the web for the answer 😉 The truth is out there.
Evan Level 33, United States of America, USA
21 January 2023
A quick article search can help fill in the blanks! https://codegym.cc/groups/posts/264-equals-and-hashcode-methods-best-practices
Rene Level 28, Netherlands
8 February 2024
Yes.. don't be a user. Become a developer. Solving issues becomes your drive! And to add something useful: use an IDE (like IntelliJ). It gives you a lot of information on classes and methods available while you are writing your code.
Tomasz Enyeart Level 16, United States
9 February 2024
Rene but people pay for this cousre and they need to provided people what they are paying for. there are countless resources on the internet but when you pay for a service and its not provided it can frustrating.