Java equals()和hashCode()

Java equals() and hashCode() methods are present in Object class. So every java class gets the default implementation of equals() and hashCode(). In this post we will look into java equals() and hashCode() methods in detail.

Java equals()和hashCode()方法存在于Object类中。 因此,每个java类都会获得equals()和hashCode()的默认实现。 在本文中,我们将详细研究java equals()和hashCode()方法。

Java equals() (Java equals())

Object class defined equals() method like this:

对象类定义了equals()方法,如下所示:

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

According to java documentation of equals() method, any implementation should adhere to following principles.

根据java文档equals()方法,任何实现都应遵循以下原则。

  • For any object x, x.equals(x) should return true.

    对于任何对象x, x.equals(x)应该返回true
  • For any two object x and y, x.equals(y) should return true if and only if y.equals(x) returns true.

    对于任何两个对象x和y, x.equals(y)应返回true当且仅当y.equals(x)返回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.

    对于x,y和z的多个对象,如果x.equals(y)返回truey.equals(z)返回true ,则x.equals(z)应该返回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.

    除非对equals()方法实现中使用的任何对象属性进行修改,否则x.equals(y)多次调用应返回相同的结果。
  • Object class equals() method implementation returns true only when both the references are pointing to same object.

    仅当两个引用都指向同一对象时,对象类的equals()方法实现才返回true

Java hashCode() (Java hashCode())

Java Object hashCode() is a native method and returns the integer hash code value of the object. The general contract of hashCode() method is:

Java Object 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.

    除非对equals()方法中使用的object属性进行修改,否则hashCode()的多次调用应返回相同的整数值。
  • 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.

    如果根据equals()方法,两个对象相等,则它们的哈希码必须相同。
  • 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()方法,两个对象不相等,则不需要要求它们的哈希码不同。 它们的哈希码值可能相等也可能不相等。

equals()和hashCode()方法的重要性 (Importance of equals() and hashCode() method)

Java hashCode() and equals() method are used in Hash table based implementations in java for storing and retrieving data. I have explained it in detail at How HashMap works in java?

Java hashCode()和equals()方法在Java中基于哈希表的实现中用于存储和检索数据。 我已经在HashMap如何在Java中工作了详细解释了

The implementation of equals() and hashCode() should follow these rules.

equals()和hashCode()的实现应遵循以下规则。

  • If o1.equals(o2), then o1.hashCode() == o2.hashCode() should always be true.

    如果o1.equals(o2) ,则o1.hashCode() == o2.hashCode()应始终为true
  • If o1.hashCode() == o2.hashCode is true, it doesn’t mean that o1.equals(o2) will be true.

    如果o1.hashCode() == o2.hashCode为true,则并不意味着o1.equals(o2)true

什么时候覆盖equals()和hashCode()方法? (When to override equals() and hashCode() methods?)

When we override equals() method, it’s almost necessary to override the hashCode() method too so that their contract is not violated by our implementation.

当我们覆盖equals()方法时,几乎也必须覆盖hashCode()方法,以免我们的实现违反其合同。

Note that your program will not throw any exceptions if the equals() and hashCode() contract is violated, if you are not planning to use the class as Hash table key, then it will not create any problem.

请注意,如果违反了equals()和hashCode()合同,则程序不会引发任何异常;如果您不打算将此类用作哈希表键,则不会造成任何问题。

If you are planning to use a class as Hash table key, then it’s must to override both equals() and hashCode() methods.

如果您打算将一个类用作哈希表键,则必须重写equals()和hashCode()方法。

Let’s see what happens when we rely on default implementation of equals() and hashCode() methods and use a custom class as HashMap key.

让我们看看当我们依靠equals()和hashCode()方法的默认实现并将自定义类用作HashMap键时会发生什么。

package com.journaldev.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.journaldev.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;
	}

}

When we run above program, it will print null. It’s because Object hashCode() method is used to find the bucket to look for the key. Since we don’t have access to the HashMap keys and we are creating the key again to retrieve the data, you will notice that hash code values of both the objects are different and hence value is not found.

当我们运行上述程序时,它将输出null 。 这是因为Object hashCode()方法用于查找存储区以查找键。 由于我们无权访问HashMap键,并且我们再次创建键以检索数据,因此您会注意到两个对象的哈希码值不同,因此找不到值。

实现equals()和hashCode()方法 (Implementing equals() and hashCode() method)

We can define our own equals() and hashCode() method implementation but if we don’t implement them carefully, it can have weird issues at runtime. Luckily most of the IDE these days provide ways to implement them automatically and if needed we can change them according to our requirement.

我们可以定义自己的equals()和hashCode()方法实现,但是如果我们不仔细实现它们,则在运行时可能会出现奇怪的问题。 幸运的是,如今,大多数IDE都提供了自动实现它们的方法,如果需要,我们可以根据需要进行更改。

We can use Eclipse to auto generate equals() and hashCode() methods.

我们可以使用Eclipse自动生成equals()和hashCode()方法。

Here is the auto generated equals() and hashCode() method implementations.

这是自动生成的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;
}

Notice that both equals() and hashCode() methods are using same fields for the calculations, so that their contract remains valid.

请注意,equals()和hashCode()方法都使用相同的字段进行计算,因此它们的合同仍然有效。

If you will run the test program again, we will get the object from map and program will print 10.

如果您再次运行测试程序,我们将从地图中获取对象,程序将打印10。

We can also use Project Lombok to auto generate equals and hashCode method implementations.

我们也可以使用Project Lombok自动生成equals和hashCode方法的实现。

什么是哈希冲突 (What is Hash Collision)

In very simple terms, Java Hash table implementations uses following logic for get and put operations.

简单来说,Java Hash表实现对获取和放置操作使用以下逻辑。

  1. First identify the “Bucket” to use using the “key” hash code.

    首先使用“密钥”哈希码识别要使用的“桶”。
  2. If there are no objects present in the bucket with same hash code, then add the object for put operation and return null for get operation.

    如果存储桶中不存在具有相同哈希码的对象,则添加该对象以进行放置操作,并返回空值以进行获取操作。
  3. If there are other objects in the bucket with same hash code, then “key” equals method comes into play.
    • If equals() return true and it’s a put operation, then object value is overridden.
    • If equals() return false and it’s a put operation, then new entry is added to the bucket.
    • If equals() return true and it’s a get operation, then object value is returned.
    • If equals() return false and it’s a get operation, then null is returned.

    如果存储桶中还有其他具有相同哈希码的对象,则“键”等于方法开始起作用。
    • 如果equals()返回true并且是put操作,则覆盖对象值。
    • 如果equals()返回false并且是put操作,那么新条目将添加到存储桶中。
    • 如果equals()返回true并且它是一个get操作,则返回对象值。
    • 如果equals()返回false并且是get操作,则返回null。

Below image shows a bucket items of HashMap and how their equals() and hashCode() are related.

下图显示了HashMap的存储桶项以及它们的equals()和hashCode()是如何关联的。

The phenomenon when two keys have same hash code is called hash collision. If hashCode() method is not implemented properly, there will be higher number of hash collision and map entries will not be properly distributed causing slowness in the get and put operations. This is the reason for prime number usage in generating hash code so that map entries are properly distributed across all the buckets.

两个键具有相同哈希码的现象称为哈希冲突。 如果hashCode()方法未正确实现,则哈希冲突数量将更多,并且映射条目将无法正确分配,从而导致get和put操作变慢。 这就是在生成哈希码时使用质数的原因,以便映射条目正确分布在所有存储桶中。

如果我们不同时实现hashCode()和equals()怎么办? (What if we don’t implement both hashCode() and equals()?)

We have already seen above that if hashCode() is not implemented, we won’t be able to retrieve the value because HashMap use hash code to find the bucket to look for the entry.

上面我们已经看到,如果未实现hashCode(),我们将无法检索该值,因为HashMap使用哈希码来查找存储桶以查找条目。

If we only use hashCode() and don’t implement equals() then also value will be not retrieved because equals() method will return false.

如果我们仅使用hashCode()而不实现equals(),那么由于equals()方法将返回false,因此不会检索到值。

实现equals()和hashCode()方法的最佳实践 (Best Practices for implementing equals() and hashCode() method)

  • Use same properties in both equals() and hashCode() method implementations, so that their contract doesn’t violate when any properties is updated.

    在equals()和hashCode()方法实现中使用相同的属性,以便在更新任何属性时都不会违反其约定。
  • 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.

    最好将不可变的对象用作哈希表键,以便我们可以缓存哈希码,而不是在每次调用时都对其进行计算。 这就是为什么String是哈希表键的理想选择的原因,因为它是不可变的,并且可以缓存哈希码值。
  • Implement hashCode() method so that least number of hash collision occurs and entries are evenly distributed across all the buckets.

    实现hashCode()方法,以使发生最少数量的哈希冲突,并且条目平均分布在所有存储桶中。
GitHub Repository. GitHub Repository下载完整的代码。

翻译自: https://www.journaldev.com/21095/java-equals-hashcode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值