Checking Object EqualityJ8 Home « Checking Object Equality

We finish the section by looking at "We finish the java OO Concepts section by looking at object equality and how to use it correctly in our classes. The equals() method of the Object class indicates whether another object is equal to the invoking object by using the == relational operator. What the method is actually doing is checking the bits in both reference variables and returning a boolean value. Therefore the equals() method of the Object class only ever returns true when the reference variables refer to the same object. Following is the contract specified in the Oracle documentation for the equals() method of the Object class:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

The equals() Method Top

To illustrate usage of the equals() method of the Object class we will make a new TestEquals class:


package info.java8;
/*
  A TestEquals class
*/ 
public class TestEquals {
    public static void main(String[] args) {
        Carrier a = new Carrier(10, 20);
        Carrier b = new Carrier(10, 20);
        Carrier c = a;
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is b = c? " + b.equals(c));
    }
}

Save, compile and run the TestEquals class in directory   c:\_OOConcepts in the usual way.

run Object equals()

The above screenshot shows the output of running our TestEquals test class. Although the instances referenced by a and b have exactly the same object state they are not the same object and so the equals() method of the Object class returns false. The c reference is initialized to refer to a and therefore refers to the same object reference and as such the equals() method of the Object class returns true.

Overriding equals() Top

But what if want to think of two objects as equal if part of their object state is shared? Well then we need to override the equals() method of the Object class in our own classes. To demonstrate how to do this we will make a new Contractor class where we will consider objects equal if they have the same name and location as part of their object state:


package info.java8;
/*
 A Contractor class
*/ 
public class Contractor {
    private String name = "";      // The Contractor Name.
    private String location = "";  // City Location.
    private String owner = "";     // The Customer Id.
    
    Contractor() {
    } 
    Contractor(String name, String location, String owner) {
        this.name = name;
        this.location = location;
        this.owner = owner;
    } 
    public boolean equals(Object obj) {
        if (this==obj) { // Same reference variable so equal
            return true;
        }
        if (!(obj instanceof Contractor)) // Make sure we have a Contractor instance
            return false;
        Contractor other = (Contractor) obj; // We need to cast to Contractor for comparison
        if (location == null) { // Compare locations
            if (other.location != null)
                return false;
        } else if (!location.equals(other.location))
            return false;
        if (name == null) { // Compare names
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

Lets go through our equals() override. The first thing we do is check for the same object using the == relational operator, if they are, we don't need to do anymore and return true. The object we want to check against is passed in polymorphically as type Object and so we need to make sure it is indeed a Contractor object and we use the instanceof operator to do this. Once we know we have the correct object type coming in we can cast it to the Contractor type for comparison. We then do checks on each property to make sure they are equal using the equals() method of String which is overridden in that class already. If all tests pass for both, then we know these two objects have the same state for the fields we are interested in and return true.

Save and compile the Contractor class in directory   c:\_OOConcepts in the usual way.

We should test our class to make sure the equals() override is working as intended:


package info.java8;
/*
  A ContractorTest class
*/ 
public class ContractorTest {
    public static void main(String[] args) {
        Contractor a = new Contractor();
        Contractor b = new Contractor();
        Contractor c = new Contractor("Contractor A", "Essex", "1234");
        Contractor d = new Contractor("Contractor B", "Essex", "1234");
        Contractor e = new Contractor("Contractor A", "Essex", "5678");
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is c = d? " + c.equals(d));
        System.out.println("Is c = e? " + c.equals(e));
        System.out.println("Is d = e? " + d.equals(e));
    }
}

Save, compile and run the ContractorTest class in directory   c:\_OOConcepts in the usual way.

run Object equals() override

The above screenshot shows the output of running our ContractorTest test class. Although the instances referenced by a and b have exactly the same object state they are not the same object and so the equals() method of the Object class returns false. The first test returns true as we haven't initialized any instance variables in a and b and so name and location are both set to null in both objects and so are indeed equal. The only other test that returns true is the fourth test where we compare c and e which both have the same name and location in both objects and so are equal. All other tests return false which is what we want so our override works correctly.

The hashCode() Method Top

The hashCode() method returns the hash code value associated with the object. Following is the contract specified in the Oracle documentation for the hashCode() method of the Object class:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode() method must consistently return the same integer, provided no information used in equals() comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode() method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

From this contract we can see that the hashCode() method of the Object class, relies on the equals() method of the Object class. What this means to us is whenever we override the equals method of the Object class, we should also override the hashCode() method to keep the general contract for the hashCode() method, which states that equal objects must have equal hash codes. We can tabularize the association between the equals and hashCode() contracts to make understanding easier:

Condition Mandatory Not Mandatory But Possible
From the equals() viewpoint
if x.equals(y) returns truex.hashCode() == y.hashCode()
if x.equals(y) returns falseNo hashCode() requirement
From the hashCode() viewpoint
x.hashCode() == y.hashCode()x.equals(y) returns true
x.hashCode() != y.hashCode()x.equals(y) returns false

So it looks like our Contractor class is breaking this contract. To see this we need to update our ContractorTest class to output the hashcodes for each object and check them against this table:


package info.java8;
/*
  A ContractorTest class
*/ 
public class ContractorTest {
    public static void main(String[] args) {
        Contractor a = new Contractor();
        Contractor b = new Contractor();
        Contractor c = new Contractor("Contractor A", "Essex", "1234");
        Contractor d = new Contractor("Contractor B", "Essex", "1234");
        Contractor e = new Contractor("Contractor A", "Essex", "5678");
        System.out.println("Is a = b? " + a.equals(b));
        System.out.println("Is a = c? " + a.equals(c));
        System.out.println("Is c = d? " + c.equals(d));
        System.out.println("Is c = e? " + c.equals(e));
        System.out.println("Is d = e? " + d.equals(e));
        System.out.println("The hashcode of a: = " + a.hashCode());
        System.out.println("The hashcode of b: = " + b.hashCode());
        System.out.println("The hashcode of c: = " + c.hashCode());
        System.out.println("The hashcode of d: = " + d.hashCode());
        System.out.println("The hashcode of e: = " + e.hashCode());
    }
}

Save, compile and run the ContractorTest class in directory   c:\_OOConcepts in the usual way.

run Object hashCode()

The above screenshot shows the output of running our ContractorTest test class. We can clearly see from this that we have broken the hashCode contract. The hash codes of a and b should be the same as should those for c and e.

Overriding hashCode() Top

Because we have overridden the equals() method of Object, we also need to override the hashCode() method. Add the new hashCode() override below to the Contractor class:


package info.java8;
/*
 A Contractor class
*/ 
public class Contractor {
     
     ...
     
	public int hashCode() {
        String hashKey = name + location;
	    return hashKey.hashCode();
	}
}

Lets go through our hashCode() override. We are concatenating the name and location instance variables we used for our equals() override into a String object. We then use the hashCode() method of the String class to convert the string to a hashcode and return it. By using the same variables we can be assured that the returned hashcode will be equal, when our equals() override returns true.

Add our hashCode() override to the Contractor class and save and compile the Contractor class in directory   c:\_OOConcepts in the usual way.

We should test our class to make sure the hashCode() override now honours its contract and is working as intended. So rerun the ContractorTest test class.

run hashCode() override

The above screenshot shows the output of running our ContractorTest test class. We can see that the hashcodes for a and b are the same, as are those for c and e. The contract for our hashCode() override is now fine.

Related Quiz

OO Concepts Quiz 20 - Checking Object Equality

Lesson 20 Complete

We finished the section on OO Concepts by looking at object equality.

What's Next?

In the next section we start our studies of exceptions by looking at an overview of exceptions.