我有一个包含不同枚举(不同类型)的类。此类用作HashMap
的键。类hashCode目前是这样实现的:
public static class Key implements Comparable<Key> {
final int a;
final Enum1 enum1;
final Enum2 enum2;
@Override
public int hashCode() {
return a ^ enum1.hashCode() ^ enum2.hashCode();
}
// ... definition of equals and toString ...
}
现在,如果枚举 hashCode 只返回枚举定义中枚举值的索引,这将不是最佳的(冲突太多)。Enum.hashCode()
的方法定义如下:
/**
* Returns a hash code for this enum constant.
*
* @return a hash code for this enum constant.
*/
public final int hashCode() {
return super.hashCode();
}
假设这委托给Object.hashCode()
,一切都应该没问题,因为对于每个枚举常量,只有一个实例,理论上Object.hashCode()
就像从对象的内部地址派生的整数一样。我说的对吗?
PS:当然,当同一个枚举在一个键中多次使用时,你将不得不使用更复杂的东西。
是的,你是对的,枚举元素的哈希代码将来自静态实例,绑定到内存位置,并且是唯一的。
另一方面,有更好的方法来生成冲突概率较小的哈希码。例如,查看 eclipse 可以为您自动生成的默认值(右键单击,来源>生成哈希代码和等于)
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((enum1 == null) ? 0 : enum1.hashCode());
result = prime * result + ((enum2 == null) ? 0 : enum2.hashCode());
return result;
}
通过将质数放入混合中(精确的数学逃脱了我),你应该更有抵抗力。
请注意,您还可以让 eclipse 为您生成一个 equals 方法!(甚至是 toString)。不是说你必须盲目信任他们,但他们通常是一个很好的开始。
如上所述,枚举在Java中是不可变的,因此,为枚举生成的哈希码是哈希集合的完美键,就像字符串是完美键一样。
枚举声明是一种特殊的类声明。枚举类型具有每个命名枚举常量的公共自类型成员。所有枚举类都有高质量的toString,hashCode和equals方法。所有这些都是可序列化的、可比较的和有效的最终版本。没有一个是可克隆的。除了 toString 之外的所有"对象方法"都是最终的:我们负责比较和序列化,并确保帽子,它做对了。
刚刚在Oracle 1.6 JVM上对此进行了测试。枚举确实委托给 Object.hashCode()。它在不同的运行之间有所不同。请记住,密钥因此在不同的 VM/VM 实例之间不稳定。因此,当您序列化 HashMap 并在其他 VM 中读回它时,您将无法使用在该 VM 中构造的键查找那里的值。
在Java 8中,您可以使用Objects.hash()
来实现此目的。
例如,您可以将hashCode
重写为
//
import static java.util.Objects.hash;
//
@Override
public int hashCode() {
return hash(a, enum1, enum2);
}