Java Enum.hashCode()背后的原因是什么?
类Enum中的方法hashCode()是final,定义为super.hashCode(),这意味着它返回一个基于实例地址的数字,该地址是程序员POV提供的随机数 将其定义为Java Enum.hashCode()背后的原因是什么?,java,enums,hash,Java,Enums,Hash,类Enum中的方法hashCode()是final,定义为super.hashCode(),这意味着它返回一个基于实例地址的数字,该地址是程序员POV提供的随机数 将其定义为ordinal()^getClass().getName().hashCode()将在不同的JVM中具有确定性。它甚至可以更好地工作,因为最低有效位将“尽可能地改变”,例如,对于包含多达16个元素的枚举和大小为16的哈希映射,将确保没有冲突(当然,使用枚举映射更好,但有时不可能,例如,没有ConcurrentEnumMap)
ordinal()^getClass().getName().hashCode()
将在不同的JVM中具有确定性。它甚至可以更好地工作,因为最低有效位将“尽可能地改变”,例如,对于包含多达16个元素的枚举和大小为16的哈希映射,将确保没有冲突(当然,使用枚举映射更好,但有时不可能,例如,没有ConcurrentEnumMap)。按照目前的定义,你没有这样的保证,是吗
答覆摘要
使用Object.hashCode()
与更好的hashCode比较如下:
- 专业人士
- 简朴
- 反对派
- 速度
- 更多冲突(对于任何大小的HashMap)
- 非决定论,传播到其他对象,使其无法用于
- 确定性模拟
- ETag计算
- 查找bug,例如根据
迭代顺序哈希集
一般来说,
hashCode
的性能有些奇怪。当我理解它们时,仍然有一个悬而未决的问题,为什么System.identityHashCode
(从对象头读取)比访问普通对象字段慢得多。只要我们不能将enum object1发送到不同的JVM,我看不出有理由对enum(以及一般的对象)提出这样的要求
1我认为这已经足够清楚了,对象是类的实例。序列化对象是字节序列,通常存储在字节数组中。我说的是一个物体 使用Object的hashCode()并将其设置为最终版本的唯一原因是让我问这个问题。 首先,您不应该依赖这些机制在JVM之间共享对象。这根本不是一个受支持的用例。当您序列化/反序列化时,您应该依赖于您自己的比较机制,或者仅将结果与您自己的JVM中的对象进行“比较” 允许枚举
hashCode
实现为对象
哈希代码(基于标识)的原因是,在一个JVM中,每个枚举对象只有一个实例。这足以确保这样的实现是合理和正确的
你可以这样争论:“嘿,字符串和原语的包装器(长、整数,…)都有定义良好的、确定性的hashCode
!为什么枚举没有它呢?”首先,您可以有几个不同的字符串引用来表示同一个字符串,这意味着使用super.hashCode
将是一个错误,因此这些类必然需要自己的hashCode实现。对于这些核心类,让它们具有定义良好的确定性哈希代码是有意义的
他们为什么选择这样解决问题?
好吧,看看。主要关注点是确保每个对象都应该返回一个不同的哈希代码(除非它等于另一个对象)。基于身份的方法非常高效,可以保证这一点,而您的建议却不能。这一要求显然比任何关于放松序列化等的“便利奖励”都要强烈。我认为他们之所以最终确定这一要求,是为了避免开发人员通过重写一个次优(甚至不正确)的哈希代码而自食其果
关于所选择的实现:它在JVM中不稳定,但速度非常快,可以避免冲突,并且不需要在枚举中添加额外的字段。鉴于enum类的实例数通常很小,且equals方法的速度很快,如果您的算法的HashMap查找时间比当前算法的要长,我也不会感到惊讶,由于其额外的复杂性。不要求JVM之间的哈希代码是确定性的,如果它们是确定性的,则不会获得任何优势。如果你依赖于这个事实,你就错了 由于每个枚举值只存在一个实例,
Object.hashcode()
保证永不冲突,代码重用性好,速度非常快
如果相等是由标识定义的,那么Object.hashcode()
将始终提供最佳性能
其他哈希代码的确定性只是其实现的副作用。由于它们的相等性通常由字段值定义,因此混合非确定性值将是浪费时间 JVM强制执行对于枚举常量,内存中只存在一个对象。您不可能在单个VM中使用相同枚举常量的两个不同实例对象,也不可能使用反射,也不可能通过序列化/反序列化跨网络使用 也就是说,因为它是唯一一个表示这个常量的对象,所以它的hascode是它的地址并不重要,因为没有其他对象可以同时占用相同的地址空间。它保证是唯一的&“确定性的”(在这个意义上,在同一个VM中,在内存中,所有对象都将具有相同的引用,