Compact Equals

Reposted from: http://www.obix-labs.com/blogs/highOctaneJava/

In this post, I will demonstrate an effective and compact way to override Java's equals(...) method. To begin, please download the reference source code for the article and open it up in your preferred IDE. When laid out in the Eclipse IDE, the project will look as follows:

Code Layout

The sample consists of two implementations of an address class, both of which are identical in every way except for their respective implementations of equals(...). The first class Address.java implements equals(...) in the more common longform fashion, whilst AddressV2.java uses my compact solution. Before looking at the alternative implementations, let us first list the attributes of these classes on which the equality test will be based:

	private Integer houseNumber;
	private String street;
	private String city;
	private String stateOrProvince;
	private String country;

Now, let us look at the longform equality test based on these attributes, as implemented in Address.java, and, which is excerpted below. As you can see, it works by initially comparing the object references and type, and then proceeds to test each individual class attribute against the matching attribute value taken from the argument. It is this later part that consumes the most real estate, and which I also find more tedious to follow and code.

	@Override
	public boolean equals(Object obj) 
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		
		Address other = (Address) obj;
		
		if (city == null) 
		{
			if (other.city != null)
				return false;
		} 
		else if (!city.equals(other.city))
			return false;
		
		if (country == null) 
		{
			if (other.country != null)
				return false;
		} 
		else if (!country.equals(other.country))
			return false;
		
		if (houseNumber == null) 
		{
			if (other.houseNumber != null)
				return false;
		} 
		else if (!houseNumber.equals(other.houseNumber))
			return false;
		
		if (stateOrProvince == null) 
		{
			if (other.stateOrProvince != null)
				return false;
		} 
		else if (!stateOrProvince.equals(other.stateOrProvince))
			return false;
		
		if (street == null) 
		{
			if (other.street != null)
				return false;
		} 
		else if (!street.equals(other.street))
			return false;
		
		return true;
	}

What if it was possible to remove the need for the longform attribute comparisons? Surely this would make the method considerably shorter and easier to follow? This is precisely the approach taken in AddressV2.java, whose equals(...) implementation is excerpted below.

	@Override
	public boolean equals(Object obj) 
	{
		boolean result;
		
		if (this == obj)
			result = true;
		else if (obj!=null && getClass() == obj.getClass())
		{
			AddressV2 other = (AddressV2) obj;
			
			Object[] fields = 
				{houseNumber, street, city, stateOrProvince, country};
			
			Object[] otherFields = 
						{other.houseNumber, other.street, other.city, 
						other.stateOrProvince, other.country};
			
			result = Arrays.equals(fields, otherFields);
		}
		else result = false;
			
		return result;
	}

I hope the reader will agree that the above implementation is not only considerably shorter but also more legible. It works simply by encapsulating the attribute values to be compared in two arrays: one for the instance on which the method is invoked; and the other for the object being compared to it. The two arrays can then simply be compared using the Arrays.equals(...) method; thus leading to a much more compact piece of code – hence the name of the technique.

Note however that if one of your class attributes is an array in itself, then you may want to consider performing the array comparison with the Arrays.deepEquals(...) method, which is better suited to comparing arrays of arbitrary depth.