2

A Deep Dive into Java 8 equals() and hashcode()

 1 year ago
source link: https://medium.com/geekculture/a-deep-dive-into-java-8-equals-and-hashcode-902990535507
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

A Deep Dive into Java 8 equals() and hashcode()

1*rOlKfuWlNAtszhr01vYfgg.jpeg

What is equals()?

In the default code in Object.java, equals() is defined as follows:

public boolean equals(Object obj) {
return (this == obj);
}

The method uses “==” to compare the two objects. “==” compares the reference addresses in Java. If both objects refer to the same address, they are equal by default.

What is hashcode()?

In the same Object.java class, hashcode() is defined as a native function.

public native int hashCode();

According to the official documented comments, hashcode():

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by java.util.HashMap.

As we can see, the purpose of hashcode() is to be used in the hash-related classes including HashMap, HashSet, HashTable, and ConcurrentHashmap.

Some questions to consider…

If two objects have the same hashcode, do they equal each other?

Not necessarily. As hashcode() returns a 32-bit integer, it is unavoidable that different objects could have the same hashcode value. A similar analogy could be that if two people have the same birthday, are they the same person?

Or the other way around, if two objects are equal, do they have the same hashcode?

Yes, they must have the same hashcode. Equal objects mean that they are referring to the same referenced object, which gives rise to the same hashcode.

Why do we need both equals() and hashcode()?

While I am crafting this article, this question pops into my mind. Then why do we still need hashcode() anyway? If we just want to compare two objects, why not just use equals()?

As discussed above, the purpose to have hashcode() method is for operations in the hash-related classess in Java. If you have read my last article about hashmap,

there is a segment of code like this:

node.hash == hash && (node.key == key || (key != null && key.equals(node.key))

What it is doing is that whenever it gets some key or puts some key into the HashMap, it will check for an existing key first. The first comparison is made between the input hash value and the existing node hash. Then the second comparison is made either for the value of the key or the key reference.

hashcode() itself is an efficient integer comparison operation. If the hash values for two objects A and B are not equal, they must be two different objects. The above code does not need to go for the second comparison and will direclty return False. This ensures the fastest comparsion between two objects. Therefore, hashcode() ensures performance.

Only if the hash values for A and B are equal, there might be a chance that they have the same reference. Therefore, equals() ensures absolute reliability.

What if you want to override equals()?

In the official Java documentation, there’s this line above the equals() method:

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

This is in line with the comparison contract in the hash-related classes. These classes need both hashcode() and equals() to ensure legitimate comparisons.

Case Study: What if you only override equals()?

In the below code, we create an object Order. We define that as long as the orderId is the same, regardless of the other fields, we will deem Order objects as equal.

public class Order {

private int orderId;
private String item;

public Order(int orderId, String item) {
this.orderId = orderId;
this.item = item;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Order order = (Order) o;
return orderId == order.orderId;
}

}

In the following code, we will create two orders, and store them into a HashSet. We will then print out the equality and the size of the hashset.

public void test(){
HashSet<Order> orders = new HashSet<>();

Order orderA = new Order(1, "bread");
Order orderB = new Order(1, "butter");

orders.add(orderA);
orders.add(orderB);

System.out.println(orderA.equals(orderB));
System.out.println(orders.size());
}

Let’s see the output.

1*EbVnMUK8OU8WK3G2bF4H5g.png
Output

In the console, the program deems the two orders as equal, but the size of the HashSet is 2.

In HashSet, a HashMap is used to record the objects added to the set, with the key as the passed object and the value as a dummy new Object, shown by the source code:

private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

Until here, hashcode() in Order is not overridden yet. HashMap comparison mechanism still takes two orders as different references, producing different hash values, even though they are defined as equal by our code. This violates the rule we mentioned earlier: if two objects are equal, their hashcode has to be equal.

Now we will override hashcode() method in Order class, in which we will take orderId as the input to calculate the hash.

@Override
public int hashCode() {
return Objects.hash(orderId);
}

Rerun the application:

1*KlakM5EJfsMDHFDTafjRPw.png
New output

OrderA and OrderB are equal and the hashset only has 1 order, which is what we expected. This is because the hashcode produced by both orders is the same because of the same orderId, which results in the same key in the map and hence the size remains 1 for the hashset.

I write about backend software engineering and I hope this article is useful to you.

I share daily learnings from work and life about Java, SQL, design patterns, and code practices weekly. Well, at least once per week. Follow me if you want to get more updates about backend engineering.

Read More:
How to Implement Deep Copy of An Object in Java
How to Auto-Generate Repetitive Java Code to Save Your Time


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK