如何确保hashcode()在Java中不解析为相同的值?
我有一个类的hashcode实现,hashcode实现与eclipse生成的内容是一致的,也是讨论过的最普遍接受的实践 下面是我的hashcode实现(此方法中使用的所有ID构成对象的键): 我遇到了一个场景,当时我正在使用一个非常大的数据集进行测试,而我的集合中没有预期数量的此类对象。仔细观察,以下两个数据集产生了相同的hashcode:50268236873,因此记录被添加到集合中的最后一个记录替换,因为它们的hashcode相同如何确保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
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文档的摘要:
a.equals(b)
,如果两个对象a
和b
相等,则哈希代码必须相同public int hashCode() {
return 0;
}
所有java.util.*
集合,如HashTable
和HashMap
都符合此合同,并且将永远不会因重复的hashcode而删除项目,即使像上面的示例中那样过度重复。这将是缓慢的,但它将是正确的
相反,添加或检索基于哈希的集合时出现意外结果的典型原因包括:
- 重用/修改对象,使其哈希代码在运行时发生更改(违反#1)
- 不重写
.equals(对象)
- 使用错误集合(在
之外),该集合假定的java.*
比合同规定的内容更多hashCode
public int hashCode() {
return 0;
}