Equality

Introduction

How simple it may seem so hard it is, testing for equality. The main reason why testing for equality is so hard in Java is that there are a lot of definitions for equality between objects.

The Java language defines the following categories which are involved in equality testing:

Primitive equality

Primitive types must be tested for equality using the == operator. The result of the expression is true if the value of both operands are the same. For example all following expressions evaluate to true:

int m = 5
int n = 5

== 1

m == n

Object reference equality

To test if two variables refer to the same object in memory the == operator must be used. For example the following expression evaluates to true

String s = new String("xyz");
String t = s;

if(t == s) {
    // evaluates to true
}

The following examples evaluate to false:

String s = new String("xyz");
String t = new String("xyz");

if(t == s) {
    // evaluates to false, though their values are identical their references are not
}

if(t == null) {
    // evaluates to false because t is not equal to null
}

Logical object equality

The equality of two objects which are not null must be reached by using the equals() method.

The method equals() is a method of the class Object and for this reason this method may be used on every object within the Java object space.

The idea behind this method is to test two objects for equality in the most intuitive way. Take, for example, the class Integer which is a representation of integer numbers. In this case the method equals is identical to the mathematical operator "=". Example:

Integer m = new Integer(#int1);
Integer n = new Integer(#int2);

if(m.equals(n)) {
    System.out.println("The condition m.equals(n) is true," +
                       " if and only if #int1 = #int2");
}

Another very simple example is the class String where the equals() method compares two strings and return true if and only if the content of both strings is identical. Some examples:

"am i equal".equals("am i equal") returns true

"first string".equals("second string") returns false

If you also take null references into account then a general test for equality looks like the following example:

Integer m = ...;
Integer n = ...;

if((m != null && m.equals(n)) || (m == n)) {
    // The one and only right test for equality
}

If and only if you have the garantuee that the variable m is not null you may simplify the example into the following:

Integer m = ...; // m is garantueed not to be null
Integer n = ...;

if(m.equals(n)) {
    // The one and only right test for equality
}

Some people tend not to use the equals() in some cases for which they actually should. For example I have seen the following code on regular basis:

Integer m = ...;
Integer n = ...;

if(m < n) {
    // do something
else if(m == n) {
    // this code will never be reached
else if(m > n) {
    // do something else
}

Due to the autoboxing in Java the comparisons of (m < n) and (m > n) are valid and are syntactically equivalent to (m.intValue() < n.intValue()) and (m.intValue() > n.intValue()) respectively. But the code (m == n) is NOT equivalent to (m.intValue() == n.intValue()).

Autoboxing and unboxing

There are some specialities with equality in combination with autoboxing and unboxing which are worth mentioning. I'll use the Integer as an example in this section.

01 /*
02  * EqualityTester.java
03  *
04  * Created on 21 augustus 2005, 14:25
05  *
06  * To change this template, choose Tools | Options and locate the template under
07  * the Source Creation and Management node. Right-click the template and choose
08  * Open. You can then make changes to the template in the Source Editor.
09  */
10 
11 package main;
12 
13 /**
14  *
15  @author Patrick Holthuizen
16  */
17 public class EqualityTester {
18     
19     /** Creates a new instance of EqualityTester */
20     public EqualityTester() {
21     }
22     
23     /**
24      @param args the command line arguments
25      */
26     public static void main(String[] args) {
27         int a = 3;
28         int b = 3;
29         
30         Integer m = 3;
31         Integer n = 3;
32         
33         Integer p = new Integer(3);
34         Integer q = new Integer(3);
35         
36         // 3 and 3 are primitives and will be compared by value
37         if(== 3) {
38             System.out.println("3 equals 3");
39         }
40         
41         // a and b are primitive types and will be compared by value
42         if(a == b) {
43             System.out.println("int a equals int b (3 equals 3)");
44         }
45         
46         // m and n are reference types and will be compared by REFERENCE, but
47         // due to autoboxing specifications that int values between -128 and 127
48         // are always boxed into FIXED objects this method may be used.
49         if(m == n) {
50             System.out.println("Integer m equals Integer n (reference to boxed 3 equals reference to boxed 3 by Java language specifications)");
51         }
52         
53         // p and q are reference types (like m and n) and will be compared by
54         // reference. Because they are assigned by using the new keyword, the
55         // variables p and q are both assigned to new and different objects so
56         // you can not compare them for equality by using the reference comparing
57         // mechanism.
58         if(p == q) {
59             // Will not occur
60         else {
61             System.out.println("Integer p does not equal Integer q (new Integer(3) is unequal to new Integer(3))");
62         }
63         
64         // of course the following always works
65         if(p.equals(q)) {
66             System.out.println("value of Integer p equals value of Integer q (3 equals 3)");
67         }
68         
69         // primitive a is boxed into an Integer 3 and the resulting references
70         // are being compared. This is only garantueed to work for values
71         // between -128 and 127.
72         if(m == a) {
73             System.out.println("reference to Integer m equals reference of boxed int a (boxed 3 equals boxed 3)");
74         }
75         
76         // primitive a is autoboxed so it becomes an object and may be used in
77         // the equals method.
78         if(m.equals(a)) {
79             System.out.println("Integer m equals boxed int a");
80         }
81     }
82     
83 }

The program above generates the following output:

3 equals 3

int a equals int b (3 equals 3)

Integer m equals Integer n (reference to boxed 3 equals reference to boxed 3 by Java language specifications)

Integer p does not equal Integer q (new Integer(3) is unequal to new Integer(3))

value of Integer p equals value of Integer q (3 equals 3)

reference to Integer m equals reference of boxed int a (boxed 3 equals boxed 3)

Integer m equals boxed int a

Conclusion

As a closing conclusion I'll give you a few rules of thumb to prevent programming errors.

Comments

If you have any comments please send an e-mail to Patrick Holthuizen.

My status