是否有可能两个实例Object
具有相同的hashCode()
?
理论上一个对象hashCode
是从它的内存地址派生的,所以所有的都hashCodes
应该是唯一的,但是如果在 GC 期间对象被移动了怎么办?
我认为对象的 hashCode 方法的文档说明了答案。
“在合理可行的情况下,Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 不需要这种实现技术编程语言。)”
给定一个合理的对象集合,很可能有两个具有相同的哈希码。在最好的情况下,它会变成生日问题,与数以万计的对象发生冲突。在实践中,对象是用相对较小的可能哈希码池创建的,并且只有数千个对象很容易发生冲突。
使用内存地址只是获得一个稍微随机数的一种方式。Sun JDK 源有一个开关,可以使用安全随机数生成器或常量。我相信 IBM(曾经?)使用快速随机数生成器,但它一点也不安全。文档中提到的内存地址似乎具有历史性质(大约十年前,具有固定位置的对象句柄并不罕见)。
这是我几年前编写的一些代码来演示冲突:
class HashClash {
public static void main(String[] args) {
final Object obj = new Object();
final int target = obj.hashCode();
Object clash;
long ct = 0;
do {
clash = new Object();
++ct;
} while (clash.hashCode() != target && ct<10L*1000*1000*1000L);
if (clash.hashCode() == target) {
System.out.println(ct+": "+obj+" - "+clash);
} else {
System.out.println("No clashes found");
}
}
}
RFE 澄清文档,因为这出现得太频繁了:CR 6321873
想想看。有无限多的潜在对象,只有 40 亿个哈希码。显然,无限的潜在对象共享每个哈希码。
Sun JVM 将Object
散列码基于对象的稳定句柄或缓存初始散列码。GC 期间的压缩不会改变hashCode()
. 如果这样做,一切都会崩溃。
是否可以?
是的。
它是否以任何合理的频率发生?
不。
我假设最初的问题只是关于默认Object
实现生成的哈希码。事实上,哈希码不能被依赖于相等性测试,并且只用于一些特定的哈希映射操作(例如那些由非常有用的HashMap
实现实现的操作)。
因此,它们不需要真正独特——它们只需要足够独特,不会产生很多冲突(这将使HashMap
实现效率低下)。
此外,预计当开发人员实现要存储在 HashMaps 中的类时,他们将实现一个哈希码算法,该算法对于同一类的对象发生冲突的可能性很小(假设您只在应用程序 HashMaps 中存储同一类的对象),并且了解数据可以更容易地实现稳健的散列。
另请参阅 Ken 的关于需要相同哈希码的相等性的回答。
您是在谈论Object
一般的实际类或对象吗?您在问题中同时使用两者。(而现实世界的应用程序通常不会创建很多实例Object
)
对于一般的对象,编写一个你想要覆盖的类是很常见的equals()
;如果你这样做,你还必须重写hashCode()
,以便该类的两个“相等”的不同实例也必须具有相同的哈希码。在这种情况下,您可能会在同一类的实例中获得“重复”的哈希码。
此外,hashCode()
在不同的类中实现时,它们通常基于对象中的某些内容,因此您最终会得到更少的“随机”值,从而导致不同类的实例之间的“重复”哈希码(无论这些对象是否“相等”) ”)。
在任何现实世界的应用程序中,找到具有相同哈希码的不同对象并不罕见。
如果哈希码与内存地址一样多,那么将需要整个内存来存储哈希本身。:-)
所以,是的,哈希码有时应该会发生重合。