equals() method


== tests for reference equality (whether they are the same object)

.equals() tests for value equality (whether they are logically "equal")

equals() is a method used to compare two objects for equality. The default implementation of the equals() method in the Object class returns true if and only if both references are pointing to the same instance. It therefore behaves the same as comparison by ==.

public class Sample {
	int field1, field2;
	String field3;
 
	public Sample(int i, int j, String k) {
		field1 = i;
		field2 = j;
		field3 = k;
	}
 
	public static void main(String[] args) {
		Sample s1 = new Sample(0, 0, "bar");
		Sample s2 = new Sample(0, 0, "bar");
		System.out.println(s1.equals(s2)); // prints false
	}
}

Even though s1 and s2 are created with the same fields, they are pointing to two different objects in memory. The default equals() implementation therefore evaluates to false.

To compare the contents of an object for equality, equals() has to be overridden.

public class Sample {
	int field1, field2;
	String field3;
 
	public Sample(int i, int j, String k) {
		field1 = i;
		field2 = j;
		field3 = k;
	}
 
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || getClass() != obj.getClass()) {
			return false;
		}
 
		Sample f = (Sample) obj;
		return field1 == f.field1 &&
			field2 == f.field2 &&
				(field3 == null ? f.field3 == null : field3.equals(f.field3));
	}
 
	@Override
	public int hashCode() {
		int hash = 1;
		hash = 31 * hash + this.field1;
		hash = 31 * hash + this.field2;
		hash = 31 * hash + (field3 == null ? 0 : field3.hashCode());
		return hash;
	}
 
	public static void main(String[] args) {
		Sample s1 = new Sample(0, 0, "bar");
		Sample s2 = new Sample(0, 0, "bar");
		System.out.println(s1.equals(s2)); // prints true
	}
}

Here the overridden equals() method decides that the objects are equal if their fields are the same.

Notice that the hashCode() method was also overwritten. The contract for that method states that when two objects are equal, their hash values must also be the same. That's why one must almost always override hashCode() and equals() together

Pay special attention to the argument type of the equals method. It is Object obj, not Sample obj. If you put the latter in your method, that is not an override of the equals method.

When writing your own class, you will have to write similar logic when overriding equals() and hashCode(). Most IDEs can automatically generate this for you.

An example of an equals() implementation can be found in the String class, which is part of the core Java API. Rather than comparing pointers, the String class compares the content of the String.

Version ≥ Java SE 1.2

Java 1.7 introduced the java.util.Objects class which provides a convenience method, equals, that compares two potentially null references, so it can be used to simplify implementations of the equals method.

@Override
public boolean equals(Object obj) {
	if (this == obj) {
		return true;
	}
	if (obj == null || getClass() != obj.getClass()) {
		return false;
	}
 
	Sample f = (Sample) obj;
	return field1 == f.field1 && field2 == f.field2 && Objects.equals(field3, f.field3);
}

Class Comparison

Since the equals method can run against any object, one of the first things the method often does (after checking for null) is to check if the class of the object being compared matches the current class.

@Override
public boolean equals(Object obj) {
	//...check for null
	if (getClass() != obj.getClass()) {
		return false;
	}
	//...compare fields
}

This is typically done as above by comparing the class objects. However, that can fail in a few special cases which may not be obvious. For example, some frameworks generate dynamic proxies of classes and these dynamic proxies are actually a different class. Here is an example using JPA.

Sample detachedInstance = ...
Sample mergedInstance = entityManager.merge(detachedInstance);
if (mergedInstance.equals(detachedInstance)) {
    //Can never get here if equality is tested with getClass()
    //as mergedInstance is a proxy (subclass) of Sample
}

One mechanism to work around that limitation is to compare classes using instanceof

@Override
public final boolean equals(Object obj) {
	if (!(obj instanceof Sample)) {
		return false;
	}
	//...compare fields
}

However, there are a few pitfalls that must be avoided when using instanceof. Since Sample could potentially have other subclasses and those subclasses might override equals() you could get into a case where a Sample is equal to a SampleSubclass but the SampleSubclass is not equal to Sample

Sample s = new Sample(7);
SampleSubclass sampleSubclass = new SampleSubclass(7, false);
s.equals(SampleSubclass) //true
SampleSubclass.equals(s) //false

This violates the properties of symmetry and transitivity and thus is an invalid implementation of the equals() method. As a result, when using instanceof, a good practice is to make the equals() method final (as in the above example). This will ensure that no subclass overrides equals() and violates key assumptions.

Basic Programs