在Java中,equals()方法和hashCode()方法的功能相同
在Object类中存在Java的equals()和hashCode()方法。因此,每个Java类都会获得equals()和hashCode()的默认实现。在本文中,我们将详细介绍Java的equals()和hashCode()方法。
Java 的 equals() 方法是相等比较的函数。
定义了对象类的equals()方法,示例如下:
public boolean equals(Object obj) {
return (this == obj);
}
根据Java文件中的equals()方法的文档,任何实现都应遵循以下原则。
- For any object x, x.equals(x) should return true.
- For any two object x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- For multiple objects x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- Multiple invocations of x.equals(y) should return same result, unless any of the object properties is modified that is being used in the equals() method implementation.
- Object class equals() method implementation returns true only when both the references are pointing to same object.
Java的hashCode()函数。
Java对象的hashCode()是一个本地方法,它返回对象的整数哈希码值。hashCode()方法的一般契约是:
- Multiple invocations of hashCode() should return the same integer value, unless the object property is modified that is being used in the equals() method.
- An object hash code value can change in multiple executions of the same application.
- If two objects are equal according to equals() method, then their hash code must be same.
- If two objects are unequal according to equals() method, their hash code are not required to be different. Their hash code value may or may-not be equal.
equals()和hashCode()方法的重要性
在Java中,hashCode()和equals()方法被用于基于哈希表的实现来存储和检索数据。我已经在《Java中的HashMap工作原理》中详细解释了它。equals()和hashCode()的实现应遵循以下规则。
- If o1.equals(o2), then o1.hashCode() == o2.hashCode() should always be true.
- If o1.hashCode() == o2.hashCode is true, it doesn’t mean that o1.equals(o2) will be true.
何时需要重写equals()和hashCode()方法?
当我们重写equals()方法时,几乎必须同时重写hashCode()方法,以避免我们的实现违反它们之间的约定。请注意,如果equals()和hashCode()的约定被违反,您的程序将不会抛出任何异常,如果您不打算将该类用作哈希表键,则不会出现任何问题。如果打算将类用作哈希表的键,则必须同时重写equals()和hashCode()方法。让我们来看看当我们依赖默认的equals()和hashCode()方法的实现,并使用自定义类作为HashMap的键时会发生什么。
package com.Olivia.java;
public class DataKey {
private String name;
private int id;
// getter and setter methods
@Override
public String toString() {
return "DataKey [name=" + name + ", id=" + id + "]";
}
}
package com.Olivia.java;
import java.util.HashMap;
import java.util.Map;
public class HashingTest {
public static void main(String[] args) {
Map<DataKey, Integer> hm = getAllData();
DataKey dk = new DataKey();
dk.setId(1);
dk.setName("Pankaj");
System.out.println(dk.hashCode());
Integer value = hm.get(dk);
System.out.println(value);
}
private static Map<DataKey, Integer> getAllData() {
Map<DataKey, Integer> hm = new HashMap<>();
DataKey dk = new DataKey();
dk.setId(1);
dk.setName("Pankaj");
System.out.println(dk.hashCode());
hm.put(dk, 10);
return hm;
}
}
当我们运行以上程序时,它会打印出null。这是因为Object类的hashCode()方法用于查找桶以寻找键。由于我们无法访问HashMap的键,并且我们正在重新创建键来检索数据,您会注意到两个对象的哈希码值是不同的,因此找不到值。
实施equals()和hashCode()方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DataKey other = (DataKey) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
请注意,equals()和hashCode()方法都使用相同的字段进行计算,以确保它们的约定仍然有效。如果您再次运行测试程序,我们将从映射中获取对象,并且程序将打印10。我们还可以使用Project Lombok自动生成equals和hashCode方法的实现。
哈希冲突是什么?
简而言之,Java的哈希表实现使用以下逻辑进行获取和放置操作。
-
- 首先,使用“key”的哈希码识别要使用的“Bucket”。
-
- 如果桶中没有具有相同哈希码的对象,则将对象添加到放操作中,并为获取操作返回null。
-
- 如果桶中有其他具有相同哈希码的对象,则使用equals()方法。
如果equals()返回true且为放操作,则覆盖对象值。
如果equals()返回false且为放操作,则将新条目添加到桶中。
如果equals()返回true且为取操作,则返回对象值。
如果equals()返回false且为取操作,则返回null。
如果我们不实现hashCode()和equals()会怎么样?
我们已经在上面看到,如果没有实现hashCode()方法,我们将无法检索到值,因为HashMap使用哈希码来找到存储条目的桶。如果我们只使用hashCode()方法而没有实现equals()方法,同样也无法检索到值,因为equals()方法会返回false。
实施equals()和hashCode()方法的最佳实践
- Use same properties in both equals() and hashCode() method implementations, so that their contract doesn’t violate when any properties is updated.
- It’s better to use immutable objects as Hash table key so that we can cache the hash code rather than calculating it on every call. That’s why String is a good candidate for Hash table key because it’s immutable and cache the hash code value.
- Implement hashCode() method so that least number of hash collision occurs and entries are evenly distributed across all the buckets.
你可以从我们的GitHub仓库下载完整的代码。