如何在Java中计算枚举的哈希码,并将枚举hashCodes与HashMap的键组合

时间:2012-11-27 09:48:18

标签: java enums hashmap hashcode

我有一个包含不同枚举(不同类型)的类。此类用作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:当在键中多次使用相同的枚举时,你将不得不使用更复杂的东西。

4 个答案:

答案 0 :(得分:11)

是的,你是对的,因为枚举元素的哈希码将来自静态实例,绑定到内存位置,并且是唯一的。

另一方面,有更好的方法来生成具有较少冲突概率的哈希码。例如,查看eclipse可以为您自动生成的默认值(右键单击,Source&gt;生成hashCode和equals)

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)。不是说你必须盲目地信任他们,但他们通常是一个非常好的开始。

答案 1 :(得分:3)

如上所述,Enum在Java中是不可变的, 因此,为Enum生成的哈希码是Hash集合的完美密钥,就像String是完美的密钥一样。

枚举声明是一种特殊的类声明。枚举类型为每个命名的枚举常量都有公共的自我类型成员。所有枚举类都具有高质量的toString,hashCode和equals方法。所有这些都是Serializable,Comparable和有效的final。没有可克隆的。除了toString之外的所有“对象方法”都是最终的:我们负责比较和序列化,并确保t 帽子它做得对。

答案 2 :(得分:2)

在Java 8中,您可以使用Objects.hash()来实现此目的。

例如,您可以将hashCode重写为

//
import static java.util.Objects.hash;

// 
@Override
public int hashCode() {
  return hash(a, enum1, enum2);
}

答案 3 :(得分:0)

刚刚在Oracle 1.6 JVM上测试过它。枚举确实委托给Object.hashCode()。它在不同的运行之间有所不同。请记住,因此密钥在不同的VM / VM实例之间不稳定。因此,当您序列化HashMap并在不同的VM中将其读回时,您将无法使用在该VM中构建的键来查找值。