Source Allies Logo

Sharing Our Passion for Technology

& Continuous Learning

<   Back to Blog

Back to Basics: hashCode() & equals()

So we all know that if we implement equals() we must override hashCode() too. But why? The best explanation of this commandment can be found in Effective Java (2nd Edition) starting on page 45.

... Failure to do so will result in a violation of the general contract for Object.hashCode, which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable. (Bloch, Effective Java, 2nd Ed.)

If you're like me, you like to see a problem in running code. Well here you go.

	@Test
	public void testHashMapRetrieval_WithoutHashCode(){
		PersonWithoutHashCode person = new PersonWithoutHashCode("first", "last");
		PersonWithoutHashCode person2 = new PersonWithoutHashCode("first", "last");
		Map<PersonWithoutHashCode, String> map = new HashMap<PersonWithoutHashCode, String>();
		map.put(person, "friend");

		assertNull(map.get(person2));
	}

	@Test
	public void testHashMapRetrieval_WithHashCode(){
		PersonWithHashCode person = new PersonWithHashCode("first", "last");
		PersonWithHashCode person2 = new PersonWithHashCode("first", "last");
		Map<PersonWithHashCode, String> map = new HashMap<PersonWithHashCode, String>();
		map.put(person, "friend");

		assertNotNull(map.get(person2));
	}

Two equivalent 'PersonWithoutHashCode' instances are not considered equivalent when the 'get' method is called on HashMap. On the other hand the 'PersonWithHashCode' instances are considered equivalent for the same method.

While you can implement equals() and hashCode() by hand or generate them with your IDE, you might consider using the Objects class within the Guava Libraries. Here is the Person class using Guava.

	@Override
	public int hashCode() {
		return Objects.hashCode(firstName, lastName);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PersonWithGuava other = (PersonWithGuava) obj;
		return Objects.equal(firstName, other.firstName)
				&& Objects.equal(lastName, other.lastName);
	}

Here's the test for this new code.

	@Test
	public void testHashMapRetrieval_WithGuava(){
		PersonWithGuava person = new PersonWithGuava("first", "last");
		PersonWithGuava person2 = new PersonWithGuava("first", "last");
		Map<PersonWithGuava, String> map = new HashMap<PersonWithGuava, String>();
		map.put(person, "friend");

		assertNotNull(map.get(person2));
	}

So why use this? This helps make our code DRY. Much of the generated code for the 'hashCode()' and 'equals()' method can be encapsulated. The Guava Library has done just that for us. If the implementation needs to be changed it can be updated in one place.

Now if you're like me, you are lazy. As a lazy person I ask myself, "Do I want to write code that I can generate?". My initial thought is "no". Luckily you can use this new library and continue to generate your 'equals()', 'hashCode()' and even your 'toString()' methods. Simply add the Guava Eclipse plugin to your favorite version of Eclipse and voila.

This plugin adds new menu options.

This is the code that the plugin generated for the Person class.

	@Override
	public int hashCode(){
		return Objects.hashCode(firstName, lastName);
	}

	@Override
	public boolean equals(Object object){
		if (object instanceof PersonWithGuava) {
			PersonWithGuava that = (PersonWithGuava) object;
			return Objects.equal(this.firstName, that.firstName)
				&& Objects.equal(this.lastName, that.lastName);
		}
		return false;
	}

While we all know that we must override 'hashCode()' and 'equals()' together, we also know that we should eliminate duplication in our code and encapsulate common functionality. Install the Guava plugin today and remove another piece of duplication from your code.

Please read the comments below before you use this plugin.

Here's the complete code HashCode.