Introduction To Equality In Java

Equality in Java is an important mechanism to know about. As you know Java has Primitive And Object / Reference Types.

In this tutorial we’ll examine what equality means for these types. Moreover, we’ll explain the difference between equality and identity in Java.

Java Equality Introduction

Let’s start with equality by looking at primitive types. So, we want to compare primitive types for equality. This can be simply achieved by using a composition of two equal signs.

1
==

Let’s now compare two integer values.

1
2
3
4
5
6
7
8
9
@Test
public void equality_PrimitiveTypes() {
    int x = 1;
    int y = 1;
    int z= 2;

    assertTrue(x == y);
    assertFalse(x == z);
}

As we can see comparing primitive types is quite straightforward using the Java equality operator.

The equality operator is a binary operator that compares it’s left operand to it’s right.

If you read the article Java Primitive Types Versus Object Types you would know that object data types normally start with an uppercase character. So knowing this we can identify an object type in Java. In this case let’s consider comparing two String object references for equality.

1
2
3
4
5
6
@Test
public void equality_Strings() {
    String str1 = "String 1";
    String str2 = "String 2";
    assertFalse(str1 == str2);
}

Running, the above code you may get a surprise, in that the equality operator did not function like we may have thought. How can this be, you may ask yourself, how can two Strings with the same value not be equal.

Well, before answering this lets move on and create a custom object by creating a simple Car class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static class Car {
    private String numberPlate;

    public Car(String numberPlate) {
        this.numberPlate = numberPlate;
    }
    public String getNumberPlate() {
        return numberPlate;
    }
}

Now, lets instantiate object instances of our Car class and compare them for equality.

1
2
3
4
5
6
7
@Test
public void equality_ObjectIdentity() {
    Car car1 = new Car("xyz-123-14");
    Car car2 = new Car("xyz-123-14");

    assertFalse(car1 == car2);
}

If we execute the above test we can see that using the equality operator with our two car instance variables returns false, although both instances contain identical data.

To understand why this is the case we need to understand Object identity in Java.

Identity

We can think of identity as meaning identical. Moreover, it means that both object references point to the same object in memory.

When we looked at primitive types we saw that when we used the equality operator to test for equality it correctly determined the equality of the types, based on the held value. Whereas, when we compared two instances of our custom Car class with identical values, it returned false.

This behavior is due to the way the equality operator works with types of primitive and reference types. If you recall a fundamental difference between Primitve and Object Types, is that variables of a primitive type hold the value directly in the memory that is assigned to the variable. Whereas, reference types hold the memory location that points to the object in memory.

Therefore, the equality operator behaves differently for primitive and reference types.

  • Primitive Types – Equality operator compares the values for equality.
  • Reference Types – Equality operator compares identity, such that both objects refer to the same object in memory – at the same memory location.

Therefore, when we compared the two car instances the equality operator returned false, as both car instances referred to different objects in memory. However, if we had of done:

1
2
Car car3 = car1;
assertTrue(car1 == car3);

We can see that the equality operator returns true, because both the car1, and car3 variables point to the same object in memory.

So, now we understand that the equality operator when used on references compares identity – compares the memory location. Whereas, when used on primitive types compares the value.

So how do we compare objects for equality based on their state – data held in fields etc, rather than pointing to the same object in memory. To do this we need to learn about Object Equality.

Object Equality

In the previous section we learned how to compare objects for identity. Now let’s look at comparing objects for equality.

To compare objects for equality we need to use the equals method which every object in Java inherits, from the java.lang.Object class.

1
2
3
Car car1 = new Car("xyz-123-14");
Car car2 = new Car("xyz-123-14");
assertFalse(car1.equals(car2));

Above we compare the car1 and car2 instances for equality using the equals method, but still the result is false. Well, this is because we did not provide a custom implementation of the equals method and by default our Car receives the default implementation from java.lang.Object.

1
2
3
4
5
public class Object {
    public boolean equals(Object obj) {
        return (this == obj);
    }
}

As we can see the default implementation uses object identity. Therefore, we must provide a custom implementation of the equals method, that realizes our idea of equality, for the Car type. First, we will create a new CarEquality class as not to interfere with our previously written example code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static class CarEquality {
    private String numberPlate;

    public CarEquality(String numberPlate) {
        this.numberPlate = numberPlate;
    }

    public String getNumberPlate() {
        return numberPlate;
    }

}

Next we’ll override the equals method with our custom implementation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }

    if (o == null || getClass() != o.getClass()) {
        return false;
    }

    CarEquality that = (CarEquality) o;
    return Objects.equals(numberPlate, that.numberPlate);
}

Above we override the equals method of our CarEquality class. In this method we say that two CarEquality instances are equal if their numberPlate instance variables are equal.

1
2
3
CarEquality car3 = new CarEquality("xyz-123-14");
CarEquality car4 = new CarEquality("xyz-123-14");
assertTrue(car3.equals(car4));

Now, if we compare our car instances using the equals method, we can see that they are equal.

Let’s quickly review the other parts of our custom equals method.

In our equals method above, we first check for identity, because if the objects have the same identity they point to the same object in memory, consequently they are equal.

1
2
3
if (this == o) {
    return true;
}

Next we test that the object we are comparing against is not null and has the same class. Therefore, if the object is null then it can’t be equal to the non null instance the equals method is invoked on. Moreover, if the object has a different class it cannot be equal.

Finally, we compare the objects instance fields that we looked at previously, that matches our idea of equality for our Car instance.

1
2
CarEquality that = (CarEquality) o;
return Objects.equals(numberPlate, that.numberPlate);

Conclusion

In this tutorial we examined equality in Java. We looked at the difference between identity and equality when it comes to Java reference types.

We also looked at the difference between Java primitive and reference types in terms of equality.