DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Generics in Java and Their Implementation
  • SmartXML: An Alternative to XPath for Complex XML Files
  • Singleton: 6 Ways To Write and Use in Java Programming
  • Java String: A Complete Guide With Examples

Trending

  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  • Blue Skies Ahead: An AI Case Study on LLM Use for a Graph Theory Related Application
  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • Immutable Secrets Management: A Zero-Trust Approach to Sensitive Data in Containers
  1. DZone
  2. Data Engineering
  3. Data
  4. Working With hashcode() and equals()

Working With hashcode() and equals()

Need to implement your own custom equality-checking mechanism? Here are some tips for when you need to override hashcode() and equals().

By 
Hussein Terek user avatar
Hussein Terek
·
Updated Jan. 02, 18 · Tutorial
Likes (56)
Comment
Save
Tweet
Share
379.7K Views

Join the DZone community and get the full member experience.

Join For Free

By default, the Java super class java.lang.Object provides two important methods for comparing objects: equals() and hashcode(). These methods become very useful when implementing interactions between several classes in large projects. In this article, we will talk about the relationship between these methods, their default implementations, and the circumstances that force developers to provide a custom implementation for each of them.

Method Definition and Default Implementation

  • equals(Object obj): a method provided by java.lang.Object that indicates whether some other object passed as an argument is "equal to" the current instance. The default implementation provided by the JDK is based on memory location — two objects are equal if and only if they are stored in the same memory address.

  • hashcode(): a method provided by java.lang.Object that returns an integer representation of the object memory address. By default, this method returns a random integer that is unique for each instance. This integer might change between several executions of the application and won't stay the same.

The Contract Between equals() and hashcode()

The default implementation is not enough to satisfy business needs, especially if we're talking about a huge application that considers two objects as equal when some business fact happens. In some business scenarios, developers provide their own implementation in order to force their own equality mechanism regardless the memory addresses.

As per the Java documentation, developers should override both methods in order to achieve a fully working equality mechanism — it's not enough to just implement the equals() method.

If two objects are equal according to the equals(Object) method, then calling the hashcode() method on each of the two objects must produce the same integer result.

In the following sections, we provide several examples that show the importance of overriding both methods and the drawbacks of overriding equals() without hashcode().

Practical Example

We define a class called Student as the following:

package com.programmer.gate.beans;

public class Student {

    private int id;
    private String name;

    public Student(int id, String name) {
        this.name = name;
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


For testing purposes, we define a main class HashcodeEquals that checks whether two instances of Student (who have the exact same attributes) are considered as equal.

public class HashcodeEquals {

    public static void main(String[] args) {
        Student alex1 = new Student(1, "Alex");
        Student alex2 = new Student(1, "Alex");

        System.out.println("alex1 hashcode = " + alex1.hashCode());
        System.out.println("alex2 hashcode = " + alex2.hashCode());
        System.out.println("Checking equality between alex1 and alex2 = " + alex1.equals(alex2));
    }
}


Output: 

alex1 hashcode = 1852704110
alex2 hashcode = 2032578917
Checking equality between alex1 and alex2 = false


Although the two instances have exactly the same attribute values, they are stored in different memory locations. Hence, they are not considered equal as per the default implementation of equals(). The same applies for hashcode() — a random unique code is generated for each instance.

Overriding equals()

For business purposes, we consider that two students are equal if they have the same ID, so we override the equals() method and provide our own implementation as the following:

@Override
public boolean equals(Object obj) {
    if (obj == null) return false;
    if (!(obj instanceof Student))
        return false;
    if (obj == this)
        return true;
    return this.getId() == ((Student) obj).getId();
}


In the above implementation, we are saying that two students are equal if and only if they are stored in the same memory address OR they have the same ID. Now if we try to run HashcodeEquals, we will get the following output:

alex1 hashcode = 2032578917
alex2 hashcode = 1531485190
Checking equality between alex1 and alex2 = true


As you noticed, overriding equals() with our custom business forces Java to consider the ID attribute when comparing two Student objects.

equals() With ArrayList

A very popular usage of equals()  is defining an array list of Student and searching for a particular student inside it. So we modified our testing class in order the achieve this.

public class HashcodeEquals {

    public static void main(String[] args) {
        Student alex = new Student(1, "Alex");

        List < Student > studentsLst = new ArrayList < Student > ();
        studentsLst.add(alex);

        System.out.println("Arraylist size = " + studentsLst.size());
        System.out.println("Arraylist contains Alex = " + studentsLst.contains(new Student(1, "Alex")));
    }
}


After running the above test, we get the following output:

Arraylist size = 1
Arraylist contains Alex = true


Overriding hashcode()

Okay, so we override equals() and we get the expected behavior — even though the hash code of the two objects are different. So, what's the purpose of overriding hashcode()?

equals() With HashSet

Let's consider a new test scenario. We want to store all the students in a HashSet, so we update HashcodeEquals as the following:

public class HashcodeEquals {

    public static void main(String[] args) {
        Student alex1 = new Student(1, "Alex");
        Student alex2 = new Student(1, "Alex");

        HashSet < Student > students = new HashSet < Student > ();
        students.add(alex1);
        students.add(alex2);

        System.out.println("HashSet size = " + students.size());
        System.out.println("HashSet contains Alex = " + students.contains(new Student(1, "Alex")));
    }
}


If we run the above test, we get the following output:

HashSet size = 2
HashSet contains Alex = false


WAIT!! We already override equals() and verify that alex1 and alex2 are equal, and we all know that HashSet stores unique objects, so why did it consider them as different objects ?

HashSet stores its elements in memory buckets. Each bucket is linked to a particular hash code. When calling students.add(alex1), Java stores alex1 inside a bucket and links it to the value of alex1.hashcode(). Now any time an element with the same hash code is inserted into the set, it will just replace alex1. However, since alex2 has a different hash code, it will be stored in a separate bucket and will be considered a totally different object.

Now when HashSet searches for an element inside it, it first generates the element's hash code and looks for a bucket which corresponds to this hash code.

Here comes the importance of overriding hashcode(), so let's override it in Student and set it to be equal to the ID so that students who have the same ID are stored in the same bucket:

@Override
public int hashCode() {
    return id;
}


Now if we try to run the same test, we get the following output:

HashSet size = 1
HashSet contains Alex = true


See the magic of hashcode()! The two elements are now considered as equal and stored in the same memory bucket, so any time you call contains() and pass a student object holding the same hash code, the set will be able to find the element.

The same is applied for HashMap, HashTable, or any data structure that uses a hashing mechanism for storing elements.

Conclusion

In order to achieve a fully working custom equality mechanism, it is mandatory to override hashcode() each time you override equals(). Follow the tips below and you'll never have leaks in your custom equality mechanism:

  • If two objects are equal, they MUST have the same hash code.
  • If two objects have the same hash code, it doesn't mean that they are equal.
  • Overriding equals() alone will make your business fail with hashing data structures like: HashSet, HashMap, HashTable ... etc.
  • Overriding hashcode() alone doesn't force Java to ignore memory addresses when comparing two objects.
Data structure Object (computer science) Memory (storage engine) Implementation

Published at DZone with permission of Hussein Terek, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Generics in Java and Their Implementation
  • SmartXML: An Alternative to XPath for Complex XML Files
  • Singleton: 6 Ways To Write and Use in Java Programming
  • Java String: A Complete Guide With Examples

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!