繁体   English   中英

如果存在hashCode(),为什么Java需要equals()?

[英]Why does Java need equals() if there is hashCode()?

如果两个对象返回相同的hashCode,这是否意味着它们相等? 还是我们需要平等来防止冲突?

我可以通过比较hashCodes来实现equals吗?

如果两个对象具有相同的hashCode,则它们不一定相等。 否则,您将发现完美的哈希函数。 但事实恰恰相反-如果对象相等,则它们必须具有相同的hashCode。

hashCodeEquals是关于对象的不同信息

考虑一个类似于哈希码是生日的人的例子,

在这种情况下,您和许多其他人有相同的b天(相同的哈希码),但是所有您都不是同一个人。

Oracle文档中所述的hashCode方法是Java中对象的数字表示形式。 该哈希码具有有限的可能值(由可以存储在int中的值表示)。

对于更复杂的类,很有可能会找到两个具有相同哈希码值的不同对象。 同样,没有人阻止您在任何课堂上这样做。

class Test {

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

}

因此,不建议通过比较哈希码来实现equals方法。 仅当可以保证每个对象都有唯一的哈希码时,才应使用它们进行比较。 在大多数情况下,唯一可以确定的是,如果使用o1.equals(o2)两个对象相等,则o1.hashCode()== o2.hashCode()

equals方法中,可以定义更复杂的逻辑来比较同一类的两个对象。

如果两个对象返回相同的hashCode,这是否意味着它们相等?

不,这并不意味着。

Objectjavadocs指出:

hashCode的一般约定为:

  • 在Java应用程序的执行过程中,只要在同一对象上多次调用它,则hashCode方法必须一致地返回相同的整数,前提是未修改该对象的equals比较中使用的信息。 ...
  • 如果根据equals(Object)方法,两个对象相等,则在两个对象中的每个对象上调用hashCode方法必须产生相同的整数结果。
  • 根据equals(java.lang.Object)方法,如果两个对象不相等,则不需要在两个对象中的每个对象上调用hashCode方法必须产生不同的整数结果。 ...

请注意突出显示的语句。 明确地说,您的问题是“否”。


还有另一种方式可以看这个。

  1. hashCode返回一个int
  2. 一个int只能采用2 32个不同的值。
  3. 如果a.hashCode() == b.hashCode()暗示a.equals(b) ,则在运行中的Java应用程序中,在任何给定时间只能有2 32个不同(即互不相等)的对象。

最后一点显然是不正确的。 确实,如果您有足够大的堆来在64位JVM中容纳2 32个 java.lang.Object ...实例,那显然是不正确的。


第三种方法是在一些著名的示例中,两个不同的两个字符串具有相同的哈希码。


假设您的假设不正确,那么随之而来的推理也是不正确的。

  1. Java 确实需要一个equals方法。
  2. 通常,仅使用hashCode不能实现equals

您也许可以使用hashCode实现更快的equals方法,但hashCode是两次调用hashCode的速度比比较两个对象的速度更快。 通常不是。

hashCodes相等->对象可能相等->需要进一步比较
hashCodes不同->对象不相等(如果hashCode实现正确)

这就是equals方法的实现方式。 首先,您检查hashCodes是否相等。 如果是,则需要检查类字段以查看它是否表示完全相同的对象。 如果hashCodes不同,则可以确保对象不相等。

如果存在hashCode(),为什么Java需要equals()?

Java需要equals()因为它是通过检查类,字段和设计人员认为属于相等测试的其他条件来测试对象相等性的方法。

hashCode()的目的是提供一个哈希值,主要供哈希表使用; 尽管它也可以用于其他目的。 返回的值基于对象的字段及其复合和/或聚合对象的哈希码。 该方法不考虑对象的类或类型。

equals()hashCode()之间的关系是一个暗示。

  • 两个相等的对象表示的哈希码相同。
  • 具有相同哈希码的两个对象并不意味着它们相等。

后者不成立有几个原因:

  • 两个不同的对象可能会返回相同的哈希码。 请记住,哈希值会将信息从大量数据折叠为较小的数目。
  • 来自不同类别且具有相似字段的两个对象很可能会使用相同类型的哈希函数,并返回相等的哈希值。 但是,它们并不相同。
  • hashCode()可以是特定于实现的,并在不同的JVM或JVM目标安装上返回不同的值。

在同一个JVM中, hashCode()可以通过首先测试已知的哈希码并且仅在相同测试实际相等的情况下用作相等性的廉价先兆。 前提是相等性测试的费用比生成哈希码的费用高得多。

我可以通过比较hashCodes来实现equals吗?

否。如上所述,相等的哈希码并不意味着相等的对象。

有时(经常?)您不这样做!

这些答案并非不正确。 但是他们并没有讲完整的故事。

一个示例是在其中创建负载类SomeClass的对象的地方,并且通过在构造函数中增加静态变量nInstanceCount或类似的值,为创建的每个实例赋予唯一的ID:

iD = nInstanceCount++;

您的哈希函数可能

int hashCode(){
    return iD;
}

和你平等的可能则是

boolean equals( Object obj ){
    if( ! ( obj instanceof SomeClass )){
        return false;
    }
    return hashCode() == obj.hashCode();
}

……在这种情况下,您认为“等于多余”的想法实际上是正确的:如果所有类的行为都这样,那么Java 10(或Java 23)可能会说,啊,让我们摆脱愚蠢的旧等equals什么意思? (NB向后兼容会消失)。

有两个要点:

  • 然后,您只能创建SomeClass MAXINT实例。 或者...您可以 ...如果您设置了一个系统来重新分配以前销毁的实例的ID。 ID通常是long而不是int ……但是这不起作用,因为hashCode()返回int

  • 这些对象中的任何一个都不能与另一个对象“相等”,因为equals =此特定类的标识 (如您所定义)。 通常这是理想的。 通常,它关闭了所有可能的途径。

您的问题的必要含义可能是,这两种方法的“恼人的”合作方式是什么? 弗莱林在回答中提到了关键点:需要使用哈希码将HashMap类的“桶”分类。 值得一读:在为HashMap类的类设计有效的“存储桶”机制时使用的大量高级数学令人震惊。 在阅读完它之后,您可能会(和我一样)对如何以及为什么要花一些思想来烦恼实现hashCode()有所了解和崇敬!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM