为什么两个具有相同值的原始双精度具有两个不同的身份哈希代码?



我了解到所有具有相同值的原语都具有相同的identityHashCode因此,当我尝试使用具有相同值的 2 个双精度时,我想identityHashCode一些 primitives.so,它给出了不同的identityHashCode我做了以下操作:

int xInt=5;
int yInt=5;
System.out.println(System.identityHashCode(xInt));//output:1867083167
System.out.println(System.identityHashCode(yInt));//output:1867083167
double double1=5;
double double2=5;

System.out.println(System.identityHashCode(double1));//output:1915910607
System.out.println(System.identityHashCode(double2));//output:1284720968

两个具有相同值的整数具有相同的identityHashCode但具有相同值的两个双精度具有不同的identityHashCode这是为什么呢?

您的代码正在对基元值进行装箱。(基元值本身没有标识哈希代码,因为这只是一个与对象相关的概念。您的代码等效于以下内容:

int xInt=5;
int yInt=5;
Integer xInteger = xInt;
Integer yInteger = yInt;
System.out.println(System.identityHashCode(xInteger));
System.out.println(System.identityHashCode(yInteger));
double double1=5;
double double2=5;
Double boxedDouble1 = double1;
Double boxedDouble2 = double2;
System.out.println(System.identityHashCode(boxedDouble1));
System.out.println(System.identityHashCode(boxedDouble2));

现在,如果您比较参考文献本身,您会发现xInteger == yInteger是真的,但boxedDouble1 == boxedDouble2是假的......因此identityHashCode准确地代表了这种关系。

装箱整数引用引用同一对象的原因是缓存了特定范围内的装箱整型:

如果装箱的值 p 是计算布尔值、字符、短整型、

整型或长整型的常量表达式 (§15.28) 的结果,并且结果为 true、false、"\u0000"到"\u007f"范围内的字符或 -128 到 127(包括 -128 到 127)范围内的整数,则让 a 和 b 是 p 的任意两次装箱转换的结果。总是这样 a == b。

在实践中,范围实际上可以更大,并且实现也可以缓存盒装双精度,但我还没有看到这种情况发生。

我了解到所有具有相同值的原语都具有相同的身份哈希代码

这不可能是真的,因为根据定义,原语不是对象,因此首先没有标识哈希代码。

调用System.identityHashCode()时,参数被框为整数或双精度

。整数装箱对常用整数使用缓存(默认从 -128 到 127)。双拳不是这样。

尝试使用大 int,您也会得到不同的结果。

这里有一个拳击问题 - 您无法将原始数据类型与identityHashCode进行比较,因为它使用Object作为其参数。

为给定对象返回与返回的哈希代码相同的哈希代码 通过默认方法 hashCode()

但当然doubleDouble不一样。

我了解到所有具有相同值的原语都具有相同的identityHashCode。

那里至少有两个误解。 首先,原语根本没有哈希代码或身份哈希代码。它是 对象 执行,例如通过自动装箱基元值获得的包装器对象

其次,正如你自己的实验所证明的那样,整体想法是完全错误的。 如果提供原语作为要System.identityHashCode()的参数,则会自动将其装箱到相应包装类的实例中,并返回结果对象的标识哈希代码。 对象的标识哈希代码是该对象在其生存期内的唯一特征,并且独立于其状态。 同时存在的两个对象没有相同的标识哈希代码。 因此,更有趣的是,您获得自动装箱double的不同身份哈希代码,而是自动装箱的小整数获得相同的身份哈希代码。

事实上,这表明 Java 为小整数值维护了Integer个对象的缓存。 它在自动装箱所涵盖范围内的值以及处理这些值的显式Integer.valueOf()调用时使用这些值。 因此,在您的示例中,每次自动装箱整数 5 时都会获得相同的对象,并且您会看到相同的标识哈希代码。 如果使用足够大的值,则不会看到相同的效果。

Java 不会对值为 5.0 的Double执行此类缓存。

另一方面,也许你只是误解了教训。 表示相同基元类型和值的不同包装器对象不具有相同的标识哈希代码,但它们具有相同的(常规)哈希代码,因为对于包装类,这由它们表示的基元值确定。 因此,请将代码的结果与此结果进行比较:

System.out.println(Double.valueOf(double1).hashCode());
System.out.println(Double.valueOf(double2).hashCode());

最新更新