【问题标题】:Creating a hashCode() Method - Java创建 hashCode() 方法 - Java
【发布时间】:2012-03-27 17:52:43
【问题描述】:

我在为我创建的类编写 hashCode() 方法时遇到了一些麻烦。此类旨在在 TreeSet 中使用,因此,它实现了 Comparable。该类具有以下变量:

public class Node implements Comparable<Node> {
   Matrix matrix;
   int[] coordinates= new int[2];
   Node father;
   int depth;
   int cost;

这是compareTo() 方法的实现。我希望 TreeSet 按成本组织这些 Node 结构,因此,compareTo() 返回简单减法的结果。

public int compareTo(Node nodeToCompare) {
    return this.cost - nodeToCompare.cost;
}

我还实现了一个equals() 方法。

public boolean equals(Object objectToCompare) {
    if(objectToCompare== this) {return true;}
    if(objectToCompare== null || objectToCompare.getClass()!= this.getClass()) {return false;}

    Node objectNode= (Node) objectToCompare;
    return this.father.equals(objectNode.father) &&
            this.depth== objectNode.depth &&
            this.cost== objectNode.cost &&
            this.matrix.equals(objectNode.matrix) &&
            Arrays.equals(this.coordinates, objectNode.coordinates);
}

说了这么多,我有几个问题:

  1. 既然我实现了一个新的equals() 方法,我应该实现一个新的hashCode() 方法吗?
  2. 如何使用这些变量实现新的 hashCode method()? (注意,Matrix 类型的变量矩阵实现了hashCode() 方法)

就是这样!

【问题讨论】:

    标签: java hash hashcode


    【解决方案1】:

    您的compareTo 方法与您的equals 方法不一致:您的compareTo 方法表示如果两个实例具有相同的cost,则它们是等价的——因此TreeSet 最多只能包含一个具有给定cost 的实例——但是您的equals 方法说它们只有在它们具有相同的cost 时才等效并且在其他各种方面都相同。

    所以,假设您的 equals 方法是正确的:

    • 您需要修复您的 compareTo 方法以与其保持一致。
    • 你需要创建一个与之一致的hashCode方法。我建议使用与java.util.List.hashCode() 相同的逻辑,这是一种以特定顺序组装组件对象的哈希码的直接且有效的方法;基本上你会写这样的东西:
      int hashCode = 1;
      hashCode = 31 * hashCode + (father == null ? 0 :father.hashCode());
      hashCode = 31 * hashCode + 深度;
      hashCode = 31 * hashCode + 成本;
      hashCode = 31 * hashCode + matrix.hashCode();
      hashCode = 31 * hashCode + java.util.Arrays.hashCode(坐标);
      返回哈希码;

    【讨论】:

    • 但是如果我改变 compareTo 方法,这不会改变 TreeSet 中事物的组织方式吗?
    • @GonçaloLourenço:是的。这就是我的观点;现在,如果n1.cost == n2.cost,那么包含n1TreeSet 不能包含n2。这真的是你想要的吗?
    • 绝对不是。我想我将不得不使用其他东西而不是 TreeSet。既然你是第一个回答我的问题,我会把你的答案标记为正确的。感谢大家的帮助!
    • 只是不同意,如果两个哈希码相等,则对象可以不相等并且树集包含两者。
    • 我有,据我了解,我需要实现这三件事才能使它与这个 Node 结构正常工作。不过,我可能错了。
    【解决方案2】:

    Intellij IDEA 可以通过“右键单击”功能来执行此操作。只要看到它做得正确就会教给你很多东西。

    在任何情况下你都应该覆盖两者。

    【讨论】:

    • @ToKra 在我的情况下,它添加了这一行 result = prime * result + getOuterType().hashCode();,但我只希望字段具有一致的哈希码(在应用程序的重新加载之间),因此,通过评论该行它可以正常工作。这是一个内部类,这就是为什么出现那条线的原因。
    【解决方案3】:

    hashCode 方法的约定规定,如果两个对象相等,则调用 hashCode() 应该会得到相同的整数结果。相反不一定是真的,即如果两个 hashCode 相同,则对象不必彼此相等。

    查看您的 equals 方法(顺便说一句,它需要变量翻译),您可以添加所有需要相等的内部成员变量的 hashCode,以便您的 equals 方法给出 true。例如

    public int hashCode() {
        return this.matrix.hashCode() + 
               this.coordinates[0] + 
               this.coordinates[1] + 
               this.father.hashCode() +
               this.depth + this.cost;               
    }
    

    以上假设矩阵和父亲永远不是空值,如果不是这样,您需要确保检查空值。

    如果您觉得更有冒险精神,您可以将上面的几个乘以一个素数,以确保您不会遇到不同数据的 hashCode 冲突(如果您在 hashTables 和 hashMaps 中使用您的类,这将有助于提高性能)。如果你需要迎合空值,上面的方法可以写成这样更好一点:

    public int hashCode() {
        return ((this.matrix == null) ? 0 : this.matrix.hashCode()) + 
               17 * this.coordinates[0] + 
               this.coordinates[1] + 
               ((this.father == null) ? 0 : this.father.hashCode()) +
               31 * this.depth + 19 * this.cost;               
    }
    

    【讨论】:

    • 请注意father 的类型是Node,所以如果它永远不是null,那么您的方法将导致无限递归和堆栈溢出。
    • 是的,我害怕那个。我想我将不得不改变一些事情。感谢您回答我的问题!
    • 是的,因此我们需要假设 a) 父亲可以为空,或者 b) 图中没有循环路径。如果以上都不是,那么我们应该将父亲排除在hashCode计算之外。
    • 刚刚发布了上述评论,我在想...... equals() 方法对于父亲有完全相同的(递归)问题。你需要认真考虑不同的父亲是否构成不同的节点,该节点的其余数据是否相同?
    • 嗯,这是一个非常好的观点。我会记住这一点,它可能是我正在寻找的游戏规则改变者。
    【解决方案4】:

    如果您的集合很小,您可以从 hashCode 方法返回常量。它用于快速查找。 hashCodes 就像保存元素的盒子。规则是:

    1. 相同的元素必须在同一个框中(具有相同的 hashCode)——当然;
    2. 不相等的元素可以在相同或不同的框中。

    然后你返回常量,你遵守这两条规则,但它会显着降低在不小的列表上的性能(因为 JVM 将在所有元素中查找,而不是仅在同一个框中的元素中查找)。但是返回常量是不好的方法。

    PS:对不起,我的写作。英语不是我的母语。

    PPS:通常你必须以与 equals 相同的方式实现 hashCode 方法(使用相同的元素)

    【讨论】:

      猜你喜欢
      • 2011-08-08
      • 2016-03-05
      • 1970-01-01
      • 2014-10-17
      • 2020-06-09
      • 1970-01-01
      • 2013-07-28
      • 2018-03-27
      • 2021-05-20
      相关资源
      最近更新 更多