Enum.hashCode()背后的原因是什么?

maa*_*nus 48 java hash enums

类Enum中的方法hashCode()是final,定义为super.hashCode(),这意味着它根据实例的地址返回一个数字,该数字是来自程序员POV的随机数.

将其定义为例如ordinal() ^ getClass().getName().hashCode()跨不同JVM的确定性.它甚至可以更好地工作,因为最低有效位会"尽可能地改变",例如,对于包含多达16个元素的枚举和大小为16的HashMap,肯定没有碰撞(当然,使用EnumMap更好,但有时不可能,例如没有ConcurrentEnumMap).根据目前的定义,你没有这样的保证,对吗?

答案摘要

使用Object.hashCode()比较如上所述的更好的hashCode,如下所示:

  • PROS
    • 简单
  • CONTRAS
    • 速度
    • 更多冲突(对于任何大小的HashMap)
    • 非确定性,传播到其他对象,使其无法使用
      • 确定性模拟
      • ETag计算
      • 根据例如HashSet迭代顺序搜寻错误

我个人更喜欢更好的hashCode,但恕我直言,没有理由权重,可能除了速度.

UPDATE

我对速度感到好奇并写了一个令人惊讶的结果基准.对于每个类的单个字段的价格,您可以使用确定性哈希码,其速度快四倍.将哈希码存储在每个字段中会更快,尽管可以忽略不计.

标准哈希码不快得多的原因是它不能成为对象的地址,因为对象被GC移动了.

更新2

一般来说,表演会有一些奇怪的事情发生hashCode.当我理解它们时,仍然存在未解决的问题,为什么System.identityHashCode(从对象标题读取)比访问普通对象字段慢.

aio*_*obe 25

使用Object的hashCode()并使其成为我可以想象的最终的唯一原因是让我问这个问题.

首先,您不应该依赖这种机制来在JVM之间共享对象.这根本不是支持的用例.序列化/反序列化时,您应该依赖自己的比较机制,或者仅将结果与您自己的JVM中的对象"比较".

让枚举hashCode实现为Objects哈希代码(基于身份)的原因是因为,在一个JVM中,每个枚举对象只有一个实例.这足以确保这种实现有意义且正确.

你可以争辩说"嘿,字符串和原语的包装(Long,Integer,...)都有明确的,确定性的,规范的hashCode!为什么枚举不具备它?" 好吧,首先,您可以使用几个不同的字符串引用来表示相同的字符串,这意味着使用super.hashCode将是一个错误,因此这些类必然需要自己的hashCode实现.对于这些核心类,让它们具有明确定义的确定性hashcode是有意义的.

他们为什么选择像这样解决它?

那么,看看实现的要求hashCode.主要关注的是确保每个对象应返回一个不同的哈希码(除非它等于另一个对象).基于身份的方法是超级有效的,并保证这一点,而你的建议没有.关于放宽序列化等方面的这一要求显然强于任何"便利奖励".


JB *_*zet 12

我认为他们最终决定的原因是避免开发人员通过重写次优(甚至不正确)的hashCode来自我攻击.

关于所选择的实现:它在JVM中不稳定,但它非常快,避免冲突,并且在枚举中不需要额外的字段.鉴于enum类的实例数量通常很少,以及equals方法的速度,如果HashMap查找时间与您的算法相比比当前更高,我不会感到惊讶,因为它具有额外的复杂性.