如何确保hashcode()在Java中不解析为相同的值?

如何确保hashcode()在Java中不解析为相同的值?,java,jakarta-ee,hashcode,java-ee-7,hash-code-uniqueness,Java,Jakarta Ee,Hashcode,Java Ee 7,Hash Code Uniqueness,我有一个类的hashcode实现,hashcode实现与eclipse生成的内容是一致的,也是讨论过的最普遍接受的实践 下面是我的hashcode实现(此方法中使用的所有ID构成对象的键): 我遇到了一个场景,当时我正在使用一个非常大的数据集进行测试,而我的集合中没有预期数量的此类对象。仔细观察,以下两个数据集产生了相同的hashcode:50268236873,因此记录被添加到集合中的最后一个记录替换,因为它们的hashcode相同 Existing record : Record@2

我有一个类的hashcode实现,hashcode实现与eclipse生成的内容是一致的,也是讨论过的最普遍接受的实践

下面是我的hashcode实现(此方法中使用的所有ID构成对象的键):

我遇到了一个场景,当时我正在使用一个非常大的数据集进行测试,而我的集合中没有预期数量的此类对象。仔细观察,以下两个数据集产生了相同的hashcode:50268236873,因此记录被添加到集合中的最后一个记录替换,因为它们的hashcode相同

  Existing record :
  Record@2c0781cd[uId=54046,rId=10967,bId=177,reId=1728,cId=50194] 

  Record being inserted into the collection :
  Record@20dad050[uId=53806,rId=18389,bId=177,reId=19026,cId=50194]

Both of these had the hashCode value = 50268236873 
所以,问题是:

1] 这是两个不同对象的哈希代码具有相同值的明显情况。那么,如何确保任何数据集都不会发生这种情况呢?素数应该更大吗?

2] 如果仔细观察,实现中的hashCode变量是int数据类型,其最大值为2^31-1=2147483647,大于为上述数据集计算的hashCode=50268236873,因此存在溢出。如果使用hashCode值的类型,是否会产生任何后果

谢谢
诺西布

编辑:

我正在使用HashSet,在阅读了发布的答案之后,我查找了equals实现,如下所示,我认为因为在equals中,我检查两个对象的hashcode是否相同,并使用它来确定它们是否是相同的对象导致了这个问题

你们谁能证实这一点

@Override
    public boolean equals(Object paramObject) {
        boolean equals = false;
        if (paramObject != null) {
            ACRecord other = (ACRecord) paramObject;
            if ((this.hashCode() == other.hashCode()) // I think this is where I am going wrong
                    || (this.uId.equals(other.getUId())
                            && this.rId.equals(other.getRId())
                            && this.reId.equals(other.getReId())
                            && this.bId.equals(other.getBId()) 
                            && this.cId.equals(other.getCId))) {
                equals = true;
            }
        }
        return equals;
    }

解决方案:我的equals方法实现错误,因为我使用hashCode来确定两个对象是否相等。更正equals方法实现解决了我的问题,因为hashset正在替换现有记录。

通常,hash代码不保证唯一性。HashMap实现通常通过在后台存储一个列表来处理冲突,但它们包含一个检查,确保列表中的所有内容都不是匹配的,而只是真正匹配的内容

换句话说,如果您执行map.get(“foo”)并且存在冲突,哈希映射将检查每个结果(未清除),以查看它是否真的与“foo”匹配。然后它只返回完全匹配的结果


还请注意,虽然hashcodes的契约规定,响应equals()的任何两个对象都应该具有相同的hashcode,但事实并非如此。

没有要求hashcode是唯一的,只是如果两个对象相等,则hashcode也必须相等

散列冲突是意料之中的,也是不可避免的,正如您所注意到的,只有2*maxint可能值,因此,如果可能的对象空间超过此数字,则一定会发生冲突

您不能将hashCode更改为long,因为它已经定义为int,并且将使用int


像hashMap或HashSet这样的集合知道可能发生的冲突,并且不受它们的影响。自定义代码也必须是防冲突的。

哈希代码通常将较大范围的值映射到较小范围的值。这意味着即使是最完美的数据哈希算法,在达到
n+1
值时也会产生冲突,其中
n
是可能的哈希值的数目(使用int作为哈希代码时为
2^32

您的实现需要通过对对象的所有成员进行完整检查来处理此类冲突,以验证它们实际上是相等的

散列通常通过减少验证结果所需的检查量来大幅减少完全检查,因为您只需要检查具有相同散列代码的值,直到找到与数据完全匹配的值,或者如果映射中不存在与数据完全匹配的值

有关哈希映射实现的简要说明,请参见答案。

以下是来自Java 8文档的摘要:

  • 对同一对象调用两次该方法必须得到相同的值(每个JVM实例)

  • 根据
    a.equals(b)
    ,如果两个对象
    a
    b
    相等,则哈希代码必须相同

  • 以下是满足上述要求的最小定义:

    public int hashCode() {
      return 0;
    }
    
    所有
    java.util.*
    集合,如
    HashTable
    HashMap
    都符合此合同,并且将永远不会因重复的hashcode而删除项目,即使像上面的示例中那样过度重复。这将是缓慢的,但它将是正确的

    相反,添加或检索基于哈希的集合时出现意外结果的典型原因包括:

    • 重用/修改对象,使其哈希代码在运行时发生更改(违反#1)
    • 不重写
      .equals(对象)
    • 使用错误集合(在
      java.*
      之外),该集合假定的
      hashCode
      比合同规定的内容更多

    哈希从来就不是完全唯一的。然而,有些散列算法更能避免冲突。正如您已经在代码中所做的那样,通常最好使用素数来帮助解决冲突。

    我不同意您的答案,因为像java这样的语言已经存在了很长时间,并且被广泛使用,如果集合不能正常工作,因为两个对象的哈希代码由于特定的实现而相同,那么这种语言将不会像it行业那样被使用和接受。应该有一个比你的答案更好的理由。@Nohsib-你误解了hashcode的契约-它是用来提供哈希的,而不是唯一的ID。如果你使用的是使用hashcode的Java集合,那么只要你也正确地实现了equals,它们就可以解决如何在没有重复和冲突的情况下存储东西的问题。真正的问题是
    public int hashCode() {
      return 0;
    }